From daab641541e78448954ff97814562bd01e6dd610 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 07:11:33 +0200 Subject: [PATCH 01/66] i2s_out: one line of i2s_out_dma_buffer() DEBUG --- components/i2s_out/esp32/dma.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 23d9ca80..2096d6f1 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -254,7 +254,7 @@ size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, s // hit dma_eof_desc? if (desc->owner || desc->eof) { - LOG_DEBUG("eof desc=%p (owner=%u eof=%u buf=%p len=%u size=%u)", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size); + LOG_DEBUG("eof desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) -> NULL[0]", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size); // unable to find a usable DMA buffer, TX buffers full *ptr = NULL; @@ -274,19 +274,17 @@ size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, s } if (desc->len + count * size > desc->size) { - LOG_DEBUG("limited desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) < count=%u size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, count, size); + LOG_DEBUG("limit desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) -> count=%u size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, count, size); // limit to available buffer size count = (desc->size - desc->len) / size; } else { - LOG_DEBUG("complete desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) >= count=%u size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, count, size); + LOG_DEBUG("full desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) -> count=%u size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, count, size); } *ptr = desc->buf + desc->len; - LOG_DEBUG("return ptr=%p count=%u size=%u", *ptr, count, size); - return count; } } From 8c647d5b4d55bb98e7cf5821ee0ec90ccb7df722 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 07:21:11 +0200 Subject: [PATCH 02/66] i2s_out: unset dma eof desc next loop to fix OUT_DSCR_ERR interrupt --- components/i2s_out/esp32/dma.c | 2 +- components/i2s_out/esp32/dma.h | 5 +++++ components/i2s_out/esp32/intr.c | 6 +++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 2096d6f1..655b75db 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -374,7 +374,7 @@ void i2s_out_dma_start(struct i2s_out *i2s_out) } i2s_out->dma_eof_desc->owner = 1; - i2s_out->dma_eof_desc->next = i2s_out->dma_eof_desc; + i2s_out->dma_eof_desc->next = NULL; for (unsigned i = 0; i < i2s_out->dma_rx_count; i++) { LOG_DEBUG("dma_rx_desc[%u]=%p: owner=%d eof=%d len=%u size=%u buf=%p next=%p", i, diff --git a/components/i2s_out/esp32/dma.h b/components/i2s_out/esp32/dma.h index a0b076a8..38a00d77 100644 --- a/components/i2s_out/esp32/dma.h +++ b/components/i2s_out/esp32/dma.h @@ -19,3 +19,8 @@ struct dma_desc { // linked list struct dma_desc *next; }; + +static inline void i2s_dma_tx_get_des_addr(i2s_dev_t *hw, uint32_t *dscr_addr) +{ + *dscr_addr = hw->out_link_dscr; +} diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index c991c31a..c2f72fb7 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -19,7 +19,11 @@ static const int i2s_irq[I2S_PORT_MAX] = { void IRAM_ATTR i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out, BaseType_t *task_wokenp) { - LOG_ISR_WARN(""); + uint32_t dscr_addr; + + i2s_dma_tx_get_des_addr(i2s_out->dev, &dscr_addr); + + LOG_ISR_WARN("desc=%p", dscr_addr); i2s_intr_clear(i2s_out->dev, I2S_OUT_DSCR_ERR_INT_CLR); } From 6c1c9a8d4c119d04851b08cd3ba89fb2bad508f1 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 07:37:08 +0200 Subject: [PATCH 03/66] i2s_out: use I2S_OUT_TOTAL_EOF_INT --- components/i2s_out/esp32/dma.c | 8 ++++---- components/i2s_out/esp32/intr.c | 28 +++++++++++++++++++++------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 655b75db..06a60703 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -196,8 +196,8 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt i2s_ll_rx_stop_link(i2s_out->dev); i2s_ll_tx_stop_link(i2s_out->dev); - i2s_intr_disable(i2s_out->dev, I2S_OUT_EOF_INT_ENA | I2S_OUT_DSCR_ERR_INT_ENA); - i2s_intr_clear(i2s_out->dev, I2S_OUT_EOF_INT_CLR | I2S_OUT_DSCR_ERR_INT_CLR); + i2s_intr_disable(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_ENA | I2S_OUT_DSCR_ERR_INT_ENA | I2S_OUT_EOF_INT_ENA); + i2s_intr_clear(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_CLR | I2S_OUT_DSCR_ERR_INT_CLR | I2S_OUT_EOF_INT_CLR); i2s_ll_enable_dma(i2s_out->dev, false); @@ -418,8 +418,8 @@ void i2s_out_dma_start(struct i2s_out *i2s_out) i2s_ll_tx_reset_fifo(i2s_out->dev); i2s_ll_tx_reset(i2s_out->dev); - i2s_intr_clear(i2s_out->dev, I2S_OUT_EOF_INT_CLR | I2S_OUT_DSCR_ERR_INT_CLR); - i2s_intr_enable(i2s_out->dev, I2S_OUT_EOF_INT_ENA | I2S_OUT_DSCR_ERR_INT_ENA); + i2s_intr_clear(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_CLR | I2S_OUT_DSCR_ERR_INT_CLR | I2S_OUT_EOF_INT_CLR); + i2s_intr_enable(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_ENA | I2S_OUT_DSCR_ERR_INT_ENA | I2S_OUT_EOF_INT_ENA); i2s_ll_enable_dma(i2s_out->dev, true); i2s_ll_start_out_link(i2s_out->dev); diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index c2f72fb7..47090678 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -17,6 +17,24 @@ static const int i2s_irq[I2S_PORT_MAX] = { [I2S_PORT_1] = ETS_I2S1_INTR_SOURCE, }; +void IRAM_ATTR i2s_intr_out_total_eof_handler(struct i2s_out *i2s_out, BaseType_t *task_wokenp) +{ + uint32_t eof_addr; + + i2s_ll_tx_get_eof_des_addr(i2s_out->dev, &eof_addr); + + LOG_ISR_DEBUG("desc=%p", eof_addr); + + // NOTE: this is unlikely to stop DMA before this repeats at least once + i2s_ll_tx_stop_link(i2s_out->dev); + i2s_ll_rx_stop_link(i2s_out->dev); + + // unblock flush() task + xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, task_wokenp); + + i2s_intr_clear(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_CLR); +} + void IRAM_ATTR i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out, BaseType_t *task_wokenp) { uint32_t dscr_addr; @@ -38,13 +56,6 @@ void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *tas LOG_ISR_DEBUG("desc=%p", eof_desc); - // NOTE: this is unlikely to stop DMA before this repeats at least once - i2s_ll_tx_stop_link(i2s_out->dev); - i2s_ll_rx_stop_link(i2s_out->dev); - - // unblock flush() task - xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, task_wokenp); - i2s_intr_clear(i2s_out->dev, I2S_OUT_EOF_INT_CLR); } @@ -72,6 +83,9 @@ void IRAM_ATTR i2s_intr_handler(void *arg) taskENTER_CRITICAL_ISR(&i2s_out->mux); + if (int_st & I2S_OUT_TOTAL_EOF_INT_ST) { + i2s_intr_out_total_eof_handler(i2s_out, &task_woken); + } if (int_st & I2S_OUT_DSCR_ERR_INT_ST) { i2s_intr_out_dscr_err_handler(i2s_out, &task_woken); } From 5f64620731b918f238bf0fff22f51b97535310fc Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 08:03:45 +0200 Subject: [PATCH 04/66] i2s_out: simplify i2s_out_dma_buffer(), use eof isr to detect free dma links --- components/i2s_out/esp32/dma.c | 73 ++++++++++++++++----------------- components/i2s_out/esp32/intr.c | 2 +- 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 06a60703..9150469e 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -47,6 +47,7 @@ void init_dma_desc(struct dma_desc *head, unsigned count, uint8_t *buf, size_t s desc->size = (size > TRUNC(DMA_DESC_SIZE_MAX, align)) ? TRUNC(DMA_DESC_SIZE_MAX, align) : size; desc->len = 0; + desc->eof = 1; // trigger I2S_OUT_EOF_INT on each DMA link desc->owner = 0; desc->buf = buf; @@ -65,17 +66,6 @@ void init_dma_desc(struct dma_desc *head, unsigned count, uint8_t *buf, size_t s } /* Prepare desc for DMA start */ -struct dma_desc *commit_dma_desc(struct dma_desc *desc) -{ - desc->owner = 1; - - if (desc->next) { - return desc->next; - } else { - return desc; - } -} - void init_dma_eof_desc(struct dma_desc *eof_desc, uint32_t value, unsigned count) { uint32_t *ptr = (uint32_t *) eof_desc->buf; @@ -85,7 +75,6 @@ void init_dma_eof_desc(struct dma_desc *eof_desc, uint32_t value, unsigned count } eof_desc->len = count * sizeof(value); - eof_desc->eof = 1; } void reinit_dma_desc(struct dma_desc *head, unsigned count, struct dma_desc *next) @@ -249,44 +238,54 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt */ size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, size_t size) { - for (;;) { - struct dma_desc *desc = i2s_out->dma_write_desc; + struct dma_desc *desc = i2s_out->dma_write_desc; - // hit dma_eof_desc? - if (desc->owner || desc->eof) { - LOG_DEBUG("eof desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) -> NULL[0]", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size); + // stop if last desc already committed + if (desc->owner) { + LOG_WARN("owned desc=%p (owner=%u eof=%u buf=%p len=%u size=%u)", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size); - // unable to find a usable DMA buffer, TX buffers full - *ptr = NULL; + // unable to find a usable DMA buffer, TX buffers full + *ptr = NULL; - // TODO: start DMA early and wait for a free buffer? - return 0; - } + return 0; + } - // can fit minimum size - if (desc->len + size > desc->size) { - LOG_DEBUG("commit desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) < size=%u -> next=%p", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, size, desc->next); + // advance to next desc if full + if (desc->len + size > desc->size) { + LOG_DEBUG("commit desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) < size=%u -> next=%p", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, size, desc->next); - // commit, try with the next desc, if available - i2s_out->dma_write_desc = commit_dma_desc(desc); + // commit, try with the next desc, if available + desc->owner = 1; - continue; + if (desc->next) { + desc = i2s_out->dma_write_desc = desc->next; } + } - if (desc->len + count * size > desc->size) { - LOG_DEBUG("limit desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) -> count=%u size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, count, size); + if (desc->len + size > desc->size) { + LOG_WARN("overflow desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) < size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, size); - // limit to available buffer size - count = (desc->size - desc->len) / size; + // unable to find a usable DMA buffer, TX buffers full + *ptr = NULL; - } else { - LOG_DEBUG("full desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) -> count=%u size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, count, size); - } + return 0; + + } else if (desc->len + count * size > desc->size) { + // limit to available buffer size + count = (desc->size - desc->len) / size; - *ptr = desc->buf + desc->len; + LOG_DEBUG("short desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) -> count=%u size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, count, size); - return count; + } else if (desc->len + count * size < desc->size) { + LOG_DEBUG("long desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) -> count=%u size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, count, size); + + } else { + LOG_DEBUG("fill desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) -> count=%u size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, count, size); } + + *ptr = desc->buf + desc->len; + + return count; } void i2s_out_dma_commit(struct i2s_out *i2s_out, unsigned count, size_t size) diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index 47090678..82e92caa 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -54,7 +54,7 @@ void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *tas struct dma_desc *eof_desc = (struct dma_desc *) eof_addr; - LOG_ISR_DEBUG("desc=%p", eof_desc); + LOG_ISR_DEBUG("desc=%p owner=%u", eof_desc, eof_desc->owner); i2s_intr_clear(i2s_out->dev, I2S_OUT_EOF_INT_CLR); } From 79539df794f03b370d4f0c41ce2622ba92d9a247 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 08:18:04 +0200 Subject: [PATCH 05/66] i2s_out: rename dma rx -> out --- components/i2s_out/esp32/dma.c | 82 +++++++++++++++++++--------------- components/i2s_out/i2s_out.c | 5 +-- components/i2s_out/i2s_out.h | 7 +-- 3 files changed, 52 insertions(+), 42 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 9150469e..3d9ed613 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -125,11 +125,11 @@ int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align, unsigne LOG_DEBUG("size=%u align=%u repeat=%u -> desc_count=%u buf_size=%u", size, align, repeat, desc_count, buf_size); // allocate single word-aligned buffer - if (!(i2s_out->dma_rx_buf = dma_malloc(buf_size))) { - LOG_ERROR("dma_malloc(dma_rx_buf)"); + if (!(i2s_out->dma_out_buf = dma_malloc(buf_size))) { + LOG_ERROR("dma_malloc(dma_out_buf)"); return -1; } else { - LOG_DEBUG("dma_rx_buf=%p[%u]", i2s_out->dma_rx_buf, buf_size); + LOG_DEBUG("dma_out_buf=%p[%u]", i2s_out->dma_out_buf, buf_size); } if (!(i2s_out->dma_eof_buf = dma_malloc(DMA_EOF_BUF_SIZE))) { LOG_ERROR("dma_malloc(dma_eof_buf)"); @@ -139,11 +139,11 @@ int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align, unsigne } // allocate DMA descriptors - if (!(i2s_out->dma_rx_desc = dma_calloc(desc_count, sizeof(*i2s_out->dma_rx_desc)))) { - LOG_ERROR("dma_calloc(dma_rx_desc)"); + if (!(i2s_out->dma_out_desc = dma_calloc(desc_count, sizeof(*i2s_out->dma_out_desc)))) { + LOG_ERROR("dma_calloc(dma_out_desc)"); return -1; } - if (repeat && !(i2s_out->dma_repeat_desc = dma_calloc(desc_count * repeat, sizeof(*i2s_out->dma_rx_desc)))) { + if (repeat && !(i2s_out->dma_repeat_desc = dma_calloc(desc_count * repeat, sizeof(*i2s_out->dma_repeat_desc)))) { LOG_ERROR("dma_calloc(dma_repeat_desc)"); return -1; } @@ -153,14 +153,14 @@ int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align, unsigne } // initialize linked list of DMA descriptors - init_dma_desc(i2s_out->dma_rx_desc, desc_count, i2s_out->dma_rx_buf, buf_size, align, NULL); + init_dma_desc(i2s_out->dma_out_desc, desc_count, i2s_out->dma_out_buf, buf_size, align, NULL); for (unsigned i = 0; i < repeat; i++) { - init_dma_desc(i2s_out->dma_repeat_desc + i * desc_count, desc_count, i2s_out->dma_rx_buf, buf_size, align, NULL); + init_dma_desc(i2s_out->dma_repeat_desc + i * desc_count, desc_count, i2s_out->dma_out_buf, buf_size, align, NULL); } init_dma_desc(i2s_out->dma_eof_desc, 1, i2s_out->dma_eof_buf, DMA_EOF_BUF_SIZE, sizeof(uint32_t), NULL); - i2s_out->dma_rx_count = desc_count; - i2s_out->dma_rx_repeat = repeat; + i2s_out->dma_out_count = desc_count; + i2s_out->dma_repeat_count = repeat; return 0; } @@ -178,7 +178,7 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt init_dma_eof_desc(i2s_out->dma_eof_desc, options->eof_value, options->eof_count); // init RX desc - reinit_dma_desc(i2s_out->dma_rx_desc, i2s_out->dma_rx_count, NULL); + reinit_dma_desc(i2s_out->dma_out_desc, i2s_out->dma_out_count, NULL); taskENTER_CRITICAL(&i2s_out->mux); @@ -197,7 +197,7 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt i2s_ll_dma_enable_owner_check(i2s_out->dev, true); i2s_ll_dma_enable_auto_write_back(i2s_out->dev, true); - i2s_ll_set_out_link_addr(i2s_out->dev, (uint32_t) i2s_out->dma_rx_desc); + i2s_ll_set_out_link_addr(i2s_out->dev, (uint32_t) i2s_out->dma_out_desc); taskEXIT_CRITICAL(&i2s_out->mux); @@ -206,7 +206,7 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt // reset write state i2s_out->dma_start = false; - i2s_out->dma_write_desc = i2s_out->dma_rx_desc; + i2s_out->dma_write_desc = i2s_out->dma_out_desc; LOG_DEBUG("dma_write_desc=%p: owner=%d eof=%d len=%u size=%u -> buf=%p next=%p", i2s_out->dma_write_desc, @@ -321,9 +321,9 @@ void i2s_out_dma_repeat(struct i2s_out *i2s_out, unsigned count) i2s_out->dma_write_desc->owner = 1; for (unsigned i = 0; i < count; i++) { - for (unsigned j = 0; j < i2s_out->dma_rx_count; j++) { - struct dma_desc *s = &i2s_out->dma_rx_desc[j]; - struct dma_desc *d = &i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j]; + for (unsigned j = 0; j < i2s_out->dma_out_count; j++) { + struct dma_desc *s = &i2s_out->dma_out_desc[j]; + struct dma_desc *d = &i2s_out->dma_repeat_desc[i * i2s_out->dma_out_count + j]; if (!s->owner) { break; @@ -353,7 +353,7 @@ int i2s_out_dma_pending(struct i2s_out *i2s_out) return 0; } - if (i2s_out->dma_write_desc != i2s_out->dma_rx_desc || i2s_out->dma_write_desc->len > 0) { + if (i2s_out->dma_write_desc != i2s_out->dma_out_desc || i2s_out->dma_write_desc->len > 0) { // write() happened return 1; } @@ -375,28 +375,31 @@ void i2s_out_dma_start(struct i2s_out *i2s_out) i2s_out->dma_eof_desc->owner = 1; i2s_out->dma_eof_desc->next = NULL; - for (unsigned i = 0; i < i2s_out->dma_rx_count; i++) { - LOG_DEBUG("dma_rx_desc[%u]=%p: owner=%d eof=%d len=%u size=%u buf=%p next=%p", i, - &i2s_out->dma_rx_desc[i], - i2s_out->dma_rx_desc[i].owner, - i2s_out->dma_rx_desc[i].eof, - i2s_out->dma_rx_desc[i].len, - i2s_out->dma_rx_desc[i].size, - i2s_out->dma_rx_desc[i].buf, - i2s_out->dma_rx_desc[i].next +#undef DEBUG +#define DEBUG 1 + + for (unsigned i = 0; i < i2s_out->dma_out_count; i++) { + LOG_DEBUG("dma_out_desc[%u]=%p: owner=%d eof=%d len=%u size=%u buf=%p next=%p", i, + &i2s_out->dma_out_desc[i], + i2s_out->dma_out_desc[i].owner, + i2s_out->dma_out_desc[i].eof, + i2s_out->dma_out_desc[i].len, + i2s_out->dma_out_desc[i].size, + i2s_out->dma_out_desc[i].buf, + i2s_out->dma_out_desc[i].next ); } - for (unsigned i = 0; i < i2s_out->dma_rx_repeat; i++) { - for (unsigned j = 0; j < i2s_out->dma_rx_count; j++) { + for (unsigned i = 0; i < i2s_out->dma_repeat_count; i++) { + for (unsigned j = 0; j < i2s_out->dma_out_count; j++) { LOG_DEBUG("dma_repeat_desc[%u][%u]=%p: owner=%d eof=%d len=%u size=%u buf=%p next=%p", i, j, - &i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j], - i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].owner, - i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].eof, - i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].len, - i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].size, - i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].buf, - i2s_out->dma_repeat_desc[i * i2s_out->dma_rx_count + j].next + &i2s_out->dma_repeat_desc[i * i2s_out->dma_out_count + j], + i2s_out->dma_repeat_desc[i * i2s_out->dma_out_count + j].owner, + i2s_out->dma_repeat_desc[i * i2s_out->dma_out_count + j].eof, + i2s_out->dma_repeat_desc[i * i2s_out->dma_out_count + j].len, + i2s_out->dma_repeat_desc[i * i2s_out->dma_out_count + j].size, + i2s_out->dma_repeat_desc[i * i2s_out->dma_out_count + j].buf, + i2s_out->dma_repeat_desc[i * i2s_out->dma_out_count + j].next ); } } @@ -438,3 +441,12 @@ int i2s_out_dma_flush(struct i2s_out *i2s_out) return 0; } + +void i2s_out_dma_free(struct i2s_out *i2s_out) +{ + free(i2s_out->dma_eof_buf); + free(i2s_out->dma_out_buf); + free(i2s_out->dma_out_desc); + free(i2s_out->dma_repeat_desc); + free(i2s_out->dma_eof_desc); +} diff --git a/components/i2s_out/i2s_out.c b/components/i2s_out/i2s_out.c index 4db3ee1e..a93f4d95 100644 --- a/components/i2s_out/i2s_out.c +++ b/components/i2s_out/i2s_out.c @@ -68,10 +68,7 @@ int i2s_out_new(struct i2s_out **i2s_outp, i2s_port_t port, size_t buffer_size, return 0; error: - free(i2s_out->dma_eof_buf); - free(i2s_out->dma_rx_buf); - free(i2s_out->dma_rx_desc); - free(i2s_out->dma_eof_desc); + i2s_out_dma_free(i2s_out); free(i2s_out); return err; diff --git a/components/i2s_out/i2s_out.h b/components/i2s_out/i2s_out.h index f7901750..ce9950f2 100644 --- a/components/i2s_out/i2s_out.h +++ b/components/i2s_out/i2s_out.h @@ -40,12 +40,12 @@ struct i2s_out { #endif /* dma */ - uint8_t *dma_rx_buf, *dma_eof_buf; - struct dma_desc *dma_rx_desc; + uint8_t *dma_out_buf, *dma_eof_buf; + struct dma_desc *dma_out_desc; struct dma_desc *dma_repeat_desc; struct dma_desc *dma_eof_desc; - unsigned dma_rx_count, dma_rx_repeat; + unsigned dma_out_count, dma_repeat_count; // pointer to software-owned dma_rx_desc used for write() struct dma_desc *dma_write_desc; @@ -63,6 +63,7 @@ void i2s_out_dma_repeat(struct i2s_out *i2s_out, unsigned count); int i2s_out_dma_pending(struct i2s_out *i2s_out); void i2s_out_dma_start(struct i2s_out *i2s_out); int i2s_out_dma_flush(struct i2s_out *i2s_out); +void i2s_out_dma_free(struct i2s_out *i2s_out); /* i2s.c */ int i2s_out_i2s_init(struct i2s_out *i2s_out); From 1ce2f8f9db3403252ce7e620ecb42dcef875a033 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 08:21:04 +0200 Subject: [PATCH 06/66] i2s_out: rename dma eof -> end --- components/i2s_out/esp32/dma.c | 66 +++++++++++++++++----------------- components/i2s_out/i2s_out.h | 4 +-- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 3d9ed613..be372820 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -20,7 +20,7 @@ // shrink size to aligment #define TRUNC(size, align) ((size) & ~((align) - 1)) -#define DMA_EOF_BUF_SIZE (DMA_DESC_SIZE_MIN) +#define DMA_END_BUF_SIZE (DMA_DESC_SIZE_MIN) /* Allocate memory from appropriate heap region for DMA */ static inline void *dma_malloc(size_t size) @@ -66,7 +66,7 @@ void init_dma_desc(struct dma_desc *head, unsigned count, uint8_t *buf, size_t s } /* Prepare desc for DMA start */ -void init_dma_eof_desc(struct dma_desc *eof_desc, uint32_t value, unsigned count) +void init_dma_end_desc(struct dma_desc *eof_desc, uint32_t value, unsigned count) { uint32_t *ptr = (uint32_t *) eof_desc->buf; @@ -131,11 +131,11 @@ int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align, unsigne } else { LOG_DEBUG("dma_out_buf=%p[%u]", i2s_out->dma_out_buf, buf_size); } - if (!(i2s_out->dma_eof_buf = dma_malloc(DMA_EOF_BUF_SIZE))) { - LOG_ERROR("dma_malloc(dma_eof_buf)"); + if (!(i2s_out->dma_end_buf = dma_malloc(DMA_END_BUF_SIZE))) { + LOG_ERROR("dma_malloc(dma_end_buf)"); return -1; } else { - LOG_DEBUG("dma_eof_buf=%p[%u]", i2s_out->dma_eof_buf, DMA_EOF_BUF_SIZE); + LOG_DEBUG("dma_end_buf=%p[%u]", i2s_out->dma_end_buf, DMA_END_BUF_SIZE); } // allocate DMA descriptors @@ -147,8 +147,8 @@ int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align, unsigne LOG_ERROR("dma_calloc(dma_repeat_desc)"); return -1; } - if (!(i2s_out->dma_eof_desc = dma_calloc(1, sizeof(*i2s_out->dma_eof_desc)))) { - LOG_ERROR("dma_calloc(dma_eof_desc)"); + if (!(i2s_out->dma_end_desc = dma_calloc(1, sizeof(*i2s_out->dma_end_desc)))) { + LOG_ERROR("dma_calloc(dma_end_desc)"); return -1; } @@ -157,7 +157,7 @@ int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align, unsigne for (unsigned i = 0; i < repeat; i++) { init_dma_desc(i2s_out->dma_repeat_desc + i * desc_count, desc_count, i2s_out->dma_out_buf, buf_size, align, NULL); } - init_dma_desc(i2s_out->dma_eof_desc, 1, i2s_out->dma_eof_buf, DMA_EOF_BUF_SIZE, sizeof(uint32_t), NULL); + init_dma_desc(i2s_out->dma_end_desc, 1, i2s_out->dma_end_buf, DMA_END_BUF_SIZE, sizeof(uint32_t), NULL); i2s_out->dma_out_count = desc_count; i2s_out->dma_repeat_count = repeat; @@ -169,13 +169,13 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt { LOG_DEBUG("..."); - if (options->eof_count * sizeof(options->eof_value) > DMA_EOF_BUF_SIZE) { - LOG_ERROR("eof_count=%u is too large for eof buf size=%u", options->eof_count, DMA_EOF_BUF_SIZE); + if (options->eof_count * sizeof(options->eof_value) > DMA_END_BUF_SIZE) { + LOG_ERROR("eof_count=%u is too large for end buf size=%u", options->eof_count, DMA_END_BUF_SIZE); return -1; } // init EOF buffer - init_dma_eof_desc(i2s_out->dma_eof_desc, options->eof_value, options->eof_count); + init_dma_end_desc(i2s_out->dma_end_desc, options->eof_value, options->eof_count); // init RX desc reinit_dma_desc(i2s_out->dma_out_desc, i2s_out->dma_out_count, NULL); @@ -218,14 +218,14 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt i2s_out->dma_write_desc->next ); - LOG_DEBUG("dma_eof_desc=%p: owner=%d eof=%d len=%u size=%u -> buf=%p next=%p", - i2s_out->dma_eof_desc, - i2s_out->dma_eof_desc->owner, - i2s_out->dma_eof_desc->eof, - i2s_out->dma_eof_desc->len, - i2s_out->dma_eof_desc->size, - i2s_out->dma_eof_desc->buf, - i2s_out->dma_eof_desc->next + LOG_DEBUG("dma_end_desc=%p: owner=%d eof=%d len=%u size=%u -> buf=%p next=%p", + i2s_out->dma_end_desc, + i2s_out->dma_end_desc->owner, + i2s_out->dma_end_desc->eof, + i2s_out->dma_end_desc->len, + i2s_out->dma_end_desc->size, + i2s_out->dma_end_desc->buf, + i2s_out->dma_end_desc->next ); return 0; @@ -342,7 +342,7 @@ void i2s_out_dma_repeat(struct i2s_out *i2s_out, unsigned count) if (nextp) { // eof - *nextp = i2s_out->dma_eof_desc; + *nextp = i2s_out->dma_end_desc; } } @@ -369,11 +369,11 @@ void i2s_out_dma_start(struct i2s_out *i2s_out) } if (!i2s_out->dma_write_desc->next) { - i2s_out->dma_write_desc->next = i2s_out->dma_eof_desc; + i2s_out->dma_write_desc->next = i2s_out->dma_end_desc; } - i2s_out->dma_eof_desc->owner = 1; - i2s_out->dma_eof_desc->next = NULL; + i2s_out->dma_end_desc->owner = 1; + i2s_out->dma_end_desc->next = NULL; #undef DEBUG #define DEBUG 1 @@ -404,14 +404,14 @@ void i2s_out_dma_start(struct i2s_out *i2s_out) } } - LOG_DEBUG("dma_eof_desc=%p: owner=%d eof=%d len=%u size=%u -> buf=%p next=%p", - i2s_out->dma_eof_desc, - i2s_out->dma_eof_desc->owner, - i2s_out->dma_eof_desc->eof, - i2s_out->dma_eof_desc->len, - i2s_out->dma_eof_desc->size, - i2s_out->dma_eof_desc->buf, - i2s_out->dma_eof_desc->next + LOG_DEBUG("dma_end_desc=%p: owner=%d eof=%d len=%u size=%u -> buf=%p next=%p", + i2s_out->dma_end_desc, + i2s_out->dma_end_desc->owner, + i2s_out->dma_end_desc->eof, + i2s_out->dma_end_desc->len, + i2s_out->dma_end_desc->size, + i2s_out->dma_end_desc->buf, + i2s_out->dma_end_desc->next ); taskENTER_CRITICAL(&i2s_out->mux); @@ -444,9 +444,9 @@ int i2s_out_dma_flush(struct i2s_out *i2s_out) void i2s_out_dma_free(struct i2s_out *i2s_out) { - free(i2s_out->dma_eof_buf); + free(i2s_out->dma_end_buf); free(i2s_out->dma_out_buf); free(i2s_out->dma_out_desc); free(i2s_out->dma_repeat_desc); - free(i2s_out->dma_eof_desc); + free(i2s_out->dma_end_desc); } diff --git a/components/i2s_out/i2s_out.h b/components/i2s_out/i2s_out.h index ce9950f2..38973293 100644 --- a/components/i2s_out/i2s_out.h +++ b/components/i2s_out/i2s_out.h @@ -40,10 +40,10 @@ struct i2s_out { #endif /* dma */ - uint8_t *dma_out_buf, *dma_eof_buf; + uint8_t *dma_out_buf, *dma_end_buf; struct dma_desc *dma_out_desc; struct dma_desc *dma_repeat_desc; - struct dma_desc *dma_eof_desc; + struct dma_desc *dma_end_desc; unsigned dma_out_count, dma_repeat_count; From 3eece4ca75104d9f8bb896fff0b219148da51941 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 16:42:29 +0200 Subject: [PATCH 07/66] i2s_out: refactor i2s_out_dma_desc/next() --- components/i2s_out/esp32/dma.c | 71 +++++++++++++++++++++------------- components/i2s_out/i2s_out.h | 2 +- 2 files changed, 45 insertions(+), 28 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index be372820..1c75d8d3 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -208,27 +208,40 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt i2s_out->dma_start = false; i2s_out->dma_write_desc = i2s_out->dma_out_desc; - LOG_DEBUG("dma_write_desc=%p: owner=%d eof=%d len=%u size=%u -> buf=%p next=%p", - i2s_out->dma_write_desc, - i2s_out->dma_write_desc->owner, - i2s_out->dma_write_desc->eof, - i2s_out->dma_write_desc->len, - i2s_out->dma_write_desc->size, - i2s_out->dma_write_desc->buf, - i2s_out->dma_write_desc->next - ); + return 0; +} - LOG_DEBUG("dma_end_desc=%p: owner=%d eof=%d len=%u size=%u -> buf=%p next=%p", - i2s_out->dma_end_desc, - i2s_out->dma_end_desc->owner, - i2s_out->dma_end_desc->eof, - i2s_out->dma_end_desc->len, - i2s_out->dma_end_desc->size, - i2s_out->dma_end_desc->buf, - i2s_out->dma_end_desc->next - ); +/* + * Return pointer to current uncommitted dma_write_desc. + */ +struct dma_desc *i2s_out_dma_desc(struct i2s_out *i2s_out) +{ + if (i2s_out->dma_write_desc->owner) { + return NULL; + } - return 0; + return i2s_out->dma_write_desc; +} + +/* + * Commit current dma_write_desc and return next. + */ +struct dma_desc *i2s_out_dma_next(struct i2s_out *i2s_out) +{ + if (!i2s_out->dma_write_desc->owner) { + // prepare for DMA + i2s_out->dma_write_desc->owner = 1; + } + + if (!i2s_out->dma_write_desc->next) { + // no more descs left + return NULL; + } + + // start using next desc + i2s_out->dma_write_desc = i2s_out->dma_write_desc->next; + + return i2s_out->dma_write_desc; } /* @@ -238,10 +251,10 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt */ size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, size_t size) { - struct dma_desc *desc = i2s_out->dma_write_desc; + struct dma_desc *desc; // stop if last desc already committed - if (desc->owner) { + if (!(desc = i2s_out_dma_desc(i2s_out))) { LOG_WARN("owned desc=%p (owner=%u eof=%u buf=%p len=%u size=%u)", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size); // unable to find a usable DMA buffer, TX buffers full @@ -255,14 +268,18 @@ size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, s LOG_DEBUG("commit desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) < size=%u -> next=%p", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, size, desc->next); // commit, try with the next desc, if available - desc->owner = 1; - - if (desc->next) { - desc = i2s_out->dma_write_desc = desc->next; - } + desc = i2s_out_dma_next(i2s_out); } - if (desc->len + size > desc->size) { + if (!desc) { + LOG_WARN("last desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) < size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, size); + + // unable to find a usable DMA buffer, TX buffers full + *ptr = NULL; + + return 0; + + } else if (desc->len + size > desc->size) { LOG_WARN("overflow desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) < size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, size); // unable to find a usable DMA buffer, TX buffers full diff --git a/components/i2s_out/i2s_out.h b/components/i2s_out/i2s_out.h index 38973293..7298bbbc 100644 --- a/components/i2s_out/i2s_out.h +++ b/components/i2s_out/i2s_out.h @@ -47,7 +47,7 @@ struct i2s_out { unsigned dma_out_count, dma_repeat_count; - // pointer to software-owned dma_rx_desc used for write() + // pointer to software-owned dma_out_desc used for write() struct dma_desc *dma_write_desc; bool dma_start; // set by i2s_out_dma_start From 4c08a5045c04c770a7698b5af154e9d05d0970e9 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 18:39:23 +0200 Subject: [PATCH 08/66] leds: optional i2s interface setup() for async operation --- components/i2s_out/include/i2s_out.h | 7 + components/leds/include/leds.h | 20 ++- components/leds/include/leds_stats.h | 1 + components/leds/interface.c | 98 ++++++++++++- components/leds/interface.h | 2 + components/leds/interfaces/i2s.h | 6 +- components/leds/interfaces/i2s/setup.c | 182 +++++++++++++++++++++++++ components/leds/interfaces/i2s/tx.c | 179 ++++-------------------- components/leds/leds.c | 29 +--- components/leds/stats.c | 1 + 10 files changed, 339 insertions(+), 186 deletions(-) create mode 100644 components/leds/interfaces/i2s/setup.c diff --git a/components/i2s_out/include/i2s_out.h b/components/i2s_out/include/i2s_out.h index b0cc0518..db4fac7f 100644 --- a/components/i2s_out/include/i2s_out.h +++ b/components/i2s_out/include/i2s_out.h @@ -226,6 +226,13 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t */ int i2s_out_repeat(struct i2s_out *i2s_out, unsigned count); +/** + * Start I2S output, do not wait for TX. + * + * Returns <0 on error, 0 on success. + */ +int i2s_out_start(struct i2s_out *i2s_out); + /** * Start I2S output, and wait for the complete TX buffer and EOF frame to be written. * diff --git a/components/leds/include/leds.h b/components/leds/include/leds.h index 51436bc1..c303cfe1 100644 --- a/components/leds/include/leds.h +++ b/components/leds/include/leds.h @@ -449,5 +449,23 @@ unsigned leds_count_total_power(struct leds *leds); */ bool leds_is_active(struct leds *leds); -/* Output frames on interface */ +/* + * Setup persistent LEDs interface. + * + * It is also possible to call leds_tx() in sync mode without setup() / close(). + * A persistently setup interface cannot be shared across multiple leds instances, but may perform better. + */ +int leds_interface_setup(struct leds *leds); + +/* + * Output frames on interface. + * + * With a persistent setup(), this does not wait for TX to complete, and leaves the leds interface open. + * Otherwise, this is equivalent to setup() -> tx -> close(), and waits for TX to complete. + */ int leds_tx(struct leds *leds); + +/* + * Close persistent LEDs interface. + */ +int leds_interface_close(struct leds *leds); diff --git a/components/leds/include/leds_stats.h b/components/leds/include/leds_stats.h index b3acff71..08f83746 100644 --- a/components/leds/include/leds_stats.h +++ b/components/leds/include/leds_stats.h @@ -9,6 +9,7 @@ struct leds_interface_i2s_stats { struct stats_timer open; struct stats_timer write; + struct stats_timer start; struct stats_timer flush; }; #endif diff --git a/components/leds/interface.c b/components/leds/interface.c index 9266a7d3..6f5f5a96 100644 --- a/components/leds/interface.c +++ b/components/leds/interface.c @@ -53,7 +53,7 @@ int leds_interface_init(union leds_interface_state *interface, const struct leds LOG_ERROR("unsupported interface=I2S for protocol=%d", options->protocol); return -1; - } else if ((err = leds_interface_i2s_init(&interface->i2s, &options->i2s, protocol_type->i2s_interface_mode, protocol_type->i2s_interface_func, leds_interface_i2s_stats(options->interface)))) { + } else if ((err = leds_interface_i2s_init(&interface->i2s, &options->i2s, protocol_type->i2s_interface_mode, protocol_type->i2s_interface_func, options->count, leds_interface_i2s_stats(options->interface)))) { LOG_ERROR("leds_interface_i2s_init"); return err; } @@ -68,3 +68,99 @@ int leds_interface_init(union leds_interface_state *interface, const struct leds return 0; } + +int leds_interface_setup(struct leds *leds) +{ + switch (leds->options.interface) { + case LEDS_INTERFACE_NONE: + return 0; + + #if CONFIG_LEDS_SPI_ENABLED + case LEDS_INTERFACE_SPI: + return 0; + #endif + + #if CONFIG_LEDS_UART_ENABLED + case LEDS_INTERFACE_UART: + return 0; + #endif + + #if CONFIG_LEDS_I2S_ENABLED + # if LEDS_I2S_INTERFACE_COUNT > 0 + case LEDS_INTERFACE_I2S0: + # endif + # if LEDS_I2S_INTERFACE_COUNT > 1 + case LEDS_INTERFACE_I2S1: + # endif + return leds_interface_i2s_setup(&leds->interface.i2s); + #endif + + default: + LOG_ERROR("unsupported interface=%#x", leds->options.interface); + return -1; + } +} + +int leds_interface_tx(struct leds *leds) +{ + switch (leds->options.interface) { + case LEDS_INTERFACE_NONE: + return 0; + + #if CONFIG_LEDS_SPI_ENABLED + case LEDS_INTERFACE_SPI: + return leds_interface_spi_tx(&leds->interface.spi, leds->pixels, leds->options.count, &leds->limit); + #endif + + #if CONFIG_LEDS_UART_ENABLED + case LEDS_INTERFACE_UART: + return leds_interface_uart_tx(&leds->interface.uart, leds->pixels, leds->options.count, &leds->limit); + #endif + + #if CONFIG_LEDS_I2S_ENABLED + # if LEDS_I2S_INTERFACE_COUNT > 0 + case LEDS_INTERFACE_I2S0: + # endif + # if LEDS_I2S_INTERFACE_COUNT > 1 + case LEDS_INTERFACE_I2S1: + # endif + return leds_interface_i2s_tx(&leds->interface.i2s, leds->pixels, leds->options.count, &leds->limit); + #endif + + default: + LOG_ERROR("unsupported interface=%#x", leds->options.interface); + return -1; + } +} + +int leds_interface_close(struct leds *leds) +{ + switch (leds->options.interface) { + case LEDS_INTERFACE_NONE: + return 0; + + #if CONFIG_LEDS_SPI_ENABLED + case LEDS_INTERFACE_SPI: + return 0; + #endif + + #if CONFIG_LEDS_UART_ENABLED + case LEDS_INTERFACE_UART: + return 0; + #endif + + #if CONFIG_LEDS_I2S_ENABLED + # if LEDS_I2S_INTERFACE_COUNT > 0 + case LEDS_INTERFACE_I2S0: + # endif + # if LEDS_I2S_INTERFACE_COUNT > 1 + case LEDS_INTERFACE_I2S1: + # endif + return leds_interface_i2s_close(&leds->interface.i2s); + #endif + + default: + LOG_ERROR("unsupported interface=%#x", leds->options.interface); + return -1; + } +} diff --git a/components/leds/interface.h b/components/leds/interface.h index cdfcb626..f063bf40 100644 --- a/components/leds/interface.h +++ b/components/leds/interface.h @@ -23,3 +23,5 @@ union leds_interface_state { struct leds_interface_uart uart; #endif }; + +int leds_interface_tx(struct leds *leds); diff --git a/components/leds/interfaces/i2s.h b/components/leds/interfaces/i2s.h index 7850e046..bbf3cc15 100644 --- a/components/leds/interfaces/i2s.h +++ b/components/leds/interfaces/i2s.h @@ -51,6 +51,8 @@ union leds_interface_i2s_func { #define LEDS_INTERFACE_I2S_FUNC(type, func) ((union leds_interface_i2s_func) { .type = func }) struct leds_interface_i2s { + bool setup; // persistent setup() + enum leds_interface_i2s_mode mode; union leds_interface_i2s_func func; union leds_interface_i2s_buf *buf; @@ -68,7 +70,9 @@ struct leds_interface_i2s { size_t leds_interface_i2s_buffer_size(enum leds_interface_i2s_mode mode, unsigned led_count, unsigned pin_count); size_t leds_interface_i2s_buffer_align(enum leds_interface_i2s_mode mode, unsigned pin_count); -int leds_interface_i2s_init(struct leds_interface_i2s *interface, const struct leds_interface_i2s_options *options, enum leds_interface_i2s_mode mode, union leds_interface_i2s_func func, struct leds_interface_i2s_stats *stats); +int leds_interface_i2s_init(struct leds_interface_i2s *interface, const struct leds_interface_i2s_options *options, enum leds_interface_i2s_mode mode, union leds_interface_i2s_func func, unsigned count, struct leds_interface_i2s_stats *stats); +int leds_interface_i2s_setup(struct leds_interface_i2s *interface); int leds_interface_i2s_tx(struct leds_interface_i2s *interface, const struct leds_color *pixels, unsigned count, const struct leds_limit *limit); +int leds_interface_i2s_close(struct leds_interface_i2s *interface); #endif diff --git a/components/leds/interfaces/i2s/setup.c b/components/leds/interfaces/i2s/setup.c new file mode 100644 index 00000000..f898f26c --- /dev/null +++ b/components/leds/interfaces/i2s/setup.c @@ -0,0 +1,182 @@ +#include "../i2s.h" +#include "../../leds.h" +#include "../../stats.h" +#include "../../gpio.h" + +#include + +int leds_interface_i2s_init(struct leds_interface_i2s *interface, const struct leds_interface_i2s_options *options, enum leds_interface_i2s_mode mode, union leds_interface_i2s_func func, unsigned count, struct leds_interface_i2s_stats *stats) +{ + interface->mode = mode; + interface->func = func; + +#if LEDS_I2S_PARALLEL_ENABLED + interface->parallel = options->parallel; +#else + interface->parallel = 0; +#endif + interface->repeat = options->repeat; + + if (!(interface->buf = calloc(1, leds_interface_i2s_buf_size(interface->mode, interface->parallel)))) { + LOG_ERROR("calloc"); + return -1; + } + + interface->i2s_out = options->i2s_out; + interface->i2s_out_options = (struct i2s_out_options) { + // shared IO pins + .pin_mutex = options->pin_mutex, + .pin_timeout = options->pin_timeout, + }; + + switch(mode) { + case LEDS_INTERFACE_I2S_MODE_32BIT_BCK: + #if LEDS_I2S_PARALLEL_ENABLED + if (options->parallel) { + interface->i2s_out_options.mode = I2S_OUT_MODE_8BIT_PARALLEL; + } else { + interface->i2s_out_options.mode = I2S_OUT_MODE_32BIT_SERIAL; + } + #else + interface->i2s_out_options.mode = I2S_OUT_MODE_32BIT_SERIAL; + #endif + + break; + + case LEDS_INTERFACE_I2S_MODE_24BIT_1U200_4X4_80UL: + case LEDS_INTERFACE_I2S_MODE_24BIT_1U250_4X4_80UL: + case LEDS_INTERFACE_I2S_MODE_32BIT_1U250_4X4_80UL: + #if LEDS_I2S_PARALLEL_ENABLED + if (options->parallel) { + interface->i2s_out_options.mode = I2S_OUT_MODE_8BIT_PARALLEL; + } else { + // using 4x4bit -> 16-bit samples + interface->i2s_out_options.mode = I2S_OUT_MODE_16BIT_SERIAL; + } + #else + interface->i2s_out_options.mode = I2S_OUT_MODE_16BIT_SERIAL; + #endif + + break; + + default: + LOG_FATAL("unknown mode=%d", mode); + } + + switch(mode) { + case LEDS_INTERFACE_I2S_MODE_32BIT_BCK: + interface->i2s_out_options.clock = i2s_out_clock(options->clock_rate); + + break; + + case LEDS_INTERFACE_I2S_MODE_24BIT_1U200_4X4_80UL: + // 3.333MHz bit clock => 0.300us per I2S bit + // four I2S bits per 1.20us protocol bit + interface->i2s_out_options.clock = I2S_OUT_CLOCK_3M333; + + break; + + case LEDS_INTERFACE_I2S_MODE_24BIT_1U250_4X4_80UL: + case LEDS_INTERFACE_I2S_MODE_32BIT_1U250_4X4_80UL: + // 3.2MHz bit clock => 0.3125us per I2S bit + // four I2S bits per 1.25us protocol bit + interface->i2s_out_options.clock = I2S_OUT_CLOCK_3M2; + + break; + + default: + LOG_FATAL("unknown mode=%d", mode); + } + + switch (mode) { + case LEDS_INTERFACE_I2S_MODE_32BIT_BCK: + // XXX: required to workaround I2S start glitch looping previous data bits + interface->i2s_out_options.eof_value = 0x00000000; + + // one clock cycle per pixel, min 32 cycles + interface->i2s_out_options.eof_count = (1 + count / 32); + + break; + + case LEDS_INTERFACE_I2S_MODE_24BIT_1U200_4X4_80UL: + case LEDS_INTERFACE_I2S_MODE_24BIT_1U250_4X4_80UL: + case LEDS_INTERFACE_I2S_MODE_32BIT_1U250_4X4_80UL: + // 1.25us per 4-bit = 2.5us per byte * four bytes per I2S sample = 10us per 32-bit I2S sample + interface->i2s_out_options.eof_value = 0x00000000; + + // hold low for 8 * 10us + interface->i2s_out_options.eof_count = 8; + + break; + + default: + LOG_FATAL("unknown mode=%d", interface->mode); + } + + +#if LEDS_I2S_GPIO_PINS_ENABLED + for (int i = 0; i < LEDS_I2S_GPIO_PINS_SIZE; i++) { + interface->i2s_out_options.bck_gpios[i] = (i < options->gpio_pins_count) ? options->clock_pins[i] : GPIO_NUM_NC; + interface->i2s_out_options.data_gpios[i] = (i < options->gpio_pins_count) ? options->data_pins[i] : GPIO_NUM_NC; + interface->i2s_out_options.inv_data_gpios[i] = (i < options->gpio_pins_count) ? options->inv_data_pins[i] : GPIO_NUM_NC; + } +#endif + +#if LEDS_I2S_PARALLEL_ENABLED + // allow multiple copies of parallel data bits on different GPIOs + interface->i2s_out_options.parallel_data_bits = options->parallel; + + if (options->parallel) { + // I2S LCD mode requires the BCK/WS signal to be inverted when routed through the GPIO matrix + // invert BCK to idle high, transition on falling edge, and sample data on rising edge + interface->i2s_out_options.bck_inv = true; + } else { + // BCK is idle low, transition on falling edge, and sample data on rising edge + interface->i2s_out_options.bck_inv = false; + } +#endif + + interface->gpio = options->gpio; + interface->stats = stats; + + return 0; +} + +int leds_interface_i2s_setup(struct leds_interface_i2s *interface) +{ + int err = 0; + + WITH_STATS_TIMER(&interface->stats->open) { + if ((err = i2s_out_open(interface->i2s_out, &interface->i2s_out_options))) { + LOG_ERROR("i2s_out_open"); + return err; + } + } + + interface->setup = true; + +#if CONFIG_LEDS_GPIO_ENABLED + leds_gpio_setup(&interface->gpio); +#endif + + return 0; +} + +int leds_interface_i2s_close(struct leds_interface_i2s *interface) +{ + int err = 0; + + interface->setup = false; + +#if CONFIG_LEDS_GPIO_ENABLED + leds_gpio_close(&interface->gpio); +#endif + + if ((err = i2s_out_close(interface->i2s_out))) { + LOG_ERROR("i2s_out_close"); + return err; + } + + return err; + +} diff --git a/components/leds/interfaces/i2s/tx.c b/components/leds/interfaces/i2s/tx.c index 95e44709..338506d0 100644 --- a/components/leds/interfaces/i2s/tx.c +++ b/components/leds/interfaces/i2s/tx.c @@ -1,7 +1,6 @@ #include "../i2s.h" #include "../../leds.h" #include "../../stats.h" -#include "../../gpio.h" #include @@ -177,157 +176,19 @@ static int leds_interface_i2s_tx_write(struct leds_interface_i2s *interface, con } } -int leds_interface_i2s_init(struct leds_interface_i2s *interface, const struct leds_interface_i2s_options *options, enum leds_interface_i2s_mode mode, union leds_interface_i2s_func func, struct leds_interface_i2s_stats *stats) -{ - interface->mode = mode; - interface->func = func; - -#if LEDS_I2S_PARALLEL_ENABLED - interface->parallel = options->parallel; -#else - interface->parallel = 0; -#endif - interface->repeat = options->repeat; - - if (!(interface->buf = calloc(1, leds_interface_i2s_buf_size(interface->mode, interface->parallel)))) { - LOG_ERROR("calloc"); - return -1; - } - - interface->i2s_out = options->i2s_out; - interface->i2s_out_options = (struct i2s_out_options) { - // shared IO pins - .pin_mutex = options->pin_mutex, - .pin_timeout = options->pin_timeout, - }; - - switch(mode) { - case LEDS_INTERFACE_I2S_MODE_32BIT_BCK: - #if LEDS_I2S_PARALLEL_ENABLED - if (options->parallel) { - interface->i2s_out_options.mode = I2S_OUT_MODE_8BIT_PARALLEL; - } else { - interface->i2s_out_options.mode = I2S_OUT_MODE_32BIT_SERIAL; - } - #else - interface->i2s_out_options.mode = I2S_OUT_MODE_32BIT_SERIAL; - #endif - - break; - - case LEDS_INTERFACE_I2S_MODE_24BIT_1U200_4X4_80UL: - case LEDS_INTERFACE_I2S_MODE_24BIT_1U250_4X4_80UL: - case LEDS_INTERFACE_I2S_MODE_32BIT_1U250_4X4_80UL: - #if LEDS_I2S_PARALLEL_ENABLED - if (options->parallel) { - interface->i2s_out_options.mode = I2S_OUT_MODE_8BIT_PARALLEL; - } else { - // using 4x4bit -> 16-bit samples - interface->i2s_out_options.mode = I2S_OUT_MODE_16BIT_SERIAL; - } - #else - interface->i2s_out_options.mode = I2S_OUT_MODE_16BIT_SERIAL; - #endif - - break; - - default: - LOG_FATAL("unknown mode=%d", mode); - } - - switch(mode) { - case LEDS_INTERFACE_I2S_MODE_32BIT_BCK: - interface->i2s_out_options.clock = i2s_out_clock(options->clock_rate); - - break; - - case LEDS_INTERFACE_I2S_MODE_24BIT_1U200_4X4_80UL: - // 3.333MHz bit clock => 0.300us per I2S bit - // four I2S bits per 1.20us protocol bit - interface->i2s_out_options.clock = I2S_OUT_CLOCK_3M333; - - break; - - case LEDS_INTERFACE_I2S_MODE_24BIT_1U250_4X4_80UL: - case LEDS_INTERFACE_I2S_MODE_32BIT_1U250_4X4_80UL: - // 3.2MHz bit clock => 0.3125us per I2S bit - // four I2S bits per 1.25us protocol bit - interface->i2s_out_options.clock = I2S_OUT_CLOCK_3M2; - - break; - - default: - LOG_FATAL("unknown mode=%d", mode); - } - -#if LEDS_I2S_GPIO_PINS_ENABLED - for (int i = 0; i < LEDS_I2S_GPIO_PINS_SIZE; i++) { - interface->i2s_out_options.bck_gpios[i] = (i < options->gpio_pins_count) ? options->clock_pins[i] : GPIO_NUM_NC; - interface->i2s_out_options.data_gpios[i] = (i < options->gpio_pins_count) ? options->data_pins[i] : GPIO_NUM_NC; - interface->i2s_out_options.inv_data_gpios[i] = (i < options->gpio_pins_count) ? options->inv_data_pins[i] : GPIO_NUM_NC; - } -#endif - -#if LEDS_I2S_PARALLEL_ENABLED - // allow multiple copies of parallel data bits on different GPIOs - interface->i2s_out_options.parallel_data_bits = options->parallel; - - if (options->parallel) { - // I2S LCD mode requires the BCK/WS signal to be inverted when routed through the GPIO matrix - // invert BCK to idle high, transition on falling edge, and sample data on rising edge - interface->i2s_out_options.bck_inv = true; - } else { - // BCK is idle low, transition on falling edge, and sample data on rising edge - interface->i2s_out_options.bck_inv = false; - } -#endif - - interface->gpio = options->gpio; - interface->stats = stats; - - return 0; -} int leds_interface_i2s_tx(struct leds_interface_i2s *interface, const struct leds_color *pixels, unsigned count, const struct leds_limit *limit) { - int err; - - switch (interface->mode) { - case LEDS_INTERFACE_I2S_MODE_32BIT_BCK: - // XXX: required to workaround I2S start glitch looping previous data bits - interface->i2s_out_options.eof_value = 0x00000000; + bool setup = !interface->setup; // sync setup() -> write -> flush -> close()? + int err = 0; - // one clock cycle per pixel, min 32 cycles - interface->i2s_out_options.eof_count = (1 + count / 32); - - break; - - case LEDS_INTERFACE_I2S_MODE_24BIT_1U200_4X4_80UL: - case LEDS_INTERFACE_I2S_MODE_24BIT_1U250_4X4_80UL: - case LEDS_INTERFACE_I2S_MODE_32BIT_1U250_4X4_80UL: - // 1.25us per 4-bit = 2.5us per byte * four bytes per I2S sample = 10us per 32-bit I2S sample - interface->i2s_out_options.eof_value = 0x00000000; - - // hold low for 8 * 10us - interface->i2s_out_options.eof_count = 8; - - break; - - default: - LOG_FATAL("unknown mode=%d", interface->mode); - } - - WITH_STATS_TIMER(&interface->stats->open) { - if ((err = i2s_out_open(interface->i2s_out, &interface->i2s_out_options))) { - LOG_ERROR("i2s_out_open"); + if (setup) { + if ((err = leds_interface_i2s_setup(interface))) { + LOG_ERROR("leds_interface_i2s_setup"); return err; } } -#if CONFIG_LEDS_GPIO_ENABLED - leds_gpio_setup(&interface->gpio); -#endif - WITH_STATS_TIMER(&interface->stats->write) { if ((err = leds_interface_i2s_tx_write(interface, pixels, count, limit))) { goto error; @@ -341,21 +202,29 @@ int leds_interface_i2s_tx(struct leds_interface_i2s *interface, const struct led } } - WITH_STATS_TIMER(&interface->stats->flush) { - if ((err = i2s_out_flush(interface->i2s_out))) { - LOG_ERROR("i2s_out_flush"); - goto error; + if (setup) { + // sync, wait for done before close + WITH_STATS_TIMER(&interface->stats->flush) { + if ((err = i2s_out_flush(interface->i2s_out))) { + LOG_ERROR("i2s_out_flush"); + goto error; + } + } + } else { + // async, do not wait for done + WITH_STATS_TIMER(&interface->stats->start) { + if ((err = i2s_out_start(interface->i2s_out))) { + LOG_ERROR("i2s_out_start"); + goto error; + } } } error: -#if CONFIG_LEDS_GPIO_ENABLED - leds_gpio_close(&interface->gpio); -#endif - - if ((err = i2s_out_close(interface->i2s_out))) { - LOG_ERROR("i2s_out_close"); - return err; + if (setup) { + if (leds_interface_i2s_close(interface)) { + LOG_WARN("leds_interface_i2s_close"); + } } return err; diff --git a/components/leds/leds.c b/components/leds/leds.c index 353245a0..9e629c6d 100644 --- a/components/leds/leds.c +++ b/components/leds/leds.c @@ -163,32 +163,5 @@ int leds_tx(struct leds *leds) { leds_limit_update(leds); - switch (leds->options.interface) { - case LEDS_INTERFACE_NONE: - return 0; - - #if CONFIG_LEDS_SPI_ENABLED - case LEDS_INTERFACE_SPI: - return leds_interface_spi_tx(&leds->interface.spi, leds->pixels, leds->options.count, &leds->limit); - #endif - - #if CONFIG_LEDS_UART_ENABLED - case LEDS_INTERFACE_UART: - return leds_interface_uart_tx(&leds->interface.uart, leds->pixels, leds->options.count, &leds->limit); - #endif - - #if CONFIG_LEDS_I2S_ENABLED - # if LEDS_I2S_INTERFACE_COUNT > 0 - case LEDS_INTERFACE_I2S0: - # endif - # if LEDS_I2S_INTERFACE_COUNT > 1 - case LEDS_INTERFACE_I2S1: - # endif - return leds_interface_i2s_tx(&leds->interface.i2s, leds->pixels, leds->options.count, &leds->limit); - #endif - - default: - LOG_ERROR("unsupported interface=%#x", leds->options.interface); - return -1; - } + return leds_interface_tx(leds); } diff --git a/components/leds/stats.c b/components/leds/stats.c index 7bfa424a..1d4d9b90 100644 --- a/components/leds/stats.c +++ b/components/leds/stats.c @@ -24,6 +24,7 @@ void leds_reset_interface_stats() #if LEDS_I2S_INTERFACE_COUNT > 1 stats_timer_init(&leds_interface_stats.i2s1.open); stats_timer_init(&leds_interface_stats.i2s1.write); + stats_timer_init(&leds_interface_stats.i2s1.start); stats_timer_init(&leds_interface_stats.i2s1.flush); #endif } From b9fb20f386879a9e1bcbbb7403fd00155fee15e9 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 19:36:51 +0200 Subject: [PATCH 09/66] main: leds setup_interface --- main/leds.c | 23 +++++++++++++++++++++++ main/leds_cmd.c | 2 ++ main/leds_config.h | 3 +++ main/leds_configtab.i | 4 ++++ main/leds_state.h | 5 +++++ main/leds_task.c | 8 ++++++++ 6 files changed, 45 insertions(+) diff --git a/main/leds.c b/main/leds.c index 015707af..d2fead68 100644 --- a/main/leds.c +++ b/main/leds.c @@ -149,6 +149,29 @@ int start_leds() return 0; } +int setup_leds(struct leds_state *state) +{ + int err; + + if (!state->leds) { + LOG_ERROR("leds%d: not initialized", state->index + 1); + return -1; + } + + if (!state->config->interface_setup) { + return 0; + } + + LOG_INFO("Setup LEDS interface in async mode"); + + if ((err = leds_interface_setup(state->leds))) { + LOG_ERROR("leds_interface_setup"); + return err; + } + + return 0; +} + int check_leds_interface(struct leds_state *state) { if (!state->leds) { diff --git a/main/leds_cmd.c b/main/leds_cmd.c index da36a16c..8253d7ea 100644 --- a/main/leds_cmd.c +++ b/main/leds_cmd.c @@ -368,12 +368,14 @@ int leds_cmd_stats(int argc, char **argv, void *ctx) #if LEDS_I2S_INTERFACE_COUNT > 0 print_stats_timer("i2s0", "open", &stats.i2s0.open); print_stats_timer("i2s0", "write", &stats.i2s0.write); + print_stats_timer("i2s0", "start", &stats.i2s0.start); print_stats_timer("i2s0", "flush", &stats.i2s0.flush); printf("\n"); #endif #if LEDS_I2S_INTERFACE_COUNT > 1 print_stats_timer("i2s1", "open", &stats.i2s1.open); print_stats_timer("i2s1", "write", &stats.i2s1.write); + print_stats_timer("i2s1", "start", &stats.i2s1.start); print_stats_timer("i2s1", "flush", &stats.i2s1.flush); printf("\n"); #endif diff --git a/main/leds_config.h b/main/leds_config.h index 869ee1dc..25733088 100644 --- a/main/leds_config.h +++ b/main/leds_config.h @@ -110,7 +110,10 @@ struct leds_config { bool enabled; int interface; + bool interface_setup; + int protocol; + uint16_t count; uint16_t limit_total, limit_group; uint16_t limit_groups; diff --git a/main/leds_configtab.i b/main/leds_configtab.i index 400091d8..0efed4d5 100644 --- a/main/leds_configtab.i +++ b/main/leds_configtab.i @@ -13,6 +13,10 @@ const struct configtab LEDS_CONFIGTAB[] = { ), .enum_type = { .value = &LEDS_CONFIG.interface, .values = leds_interface_enum }, }, + { CONFIG_TYPE_BOOL, "interface_setup", + .description = "Setup dedicated interface, operate in async mode. I2S only.", + .bool_type = { .value = &LEDS_CONFIG.interface_setup, .default_value = false }, + }, { CONFIG_TYPE_ENUM, "protocol", .enum_type = { .value = &LEDS_CONFIG.protocol, .values = leds_protocol_enum }, }, diff --git a/main/leds_state.h b/main/leds_state.h index 67435d2b..e8c91706 100644 --- a/main/leds_state.h +++ b/main/leds_state.h @@ -51,6 +51,11 @@ extern struct leds_state leds_states[LEDS_COUNT]; # endif #endif +/* + * Optional persistent leds interface setup. + */ +int setup_leds(struct leds_state *state); + /* * Update LEDs output with given USER_ACTIVITY_LEDS_* source. */ diff --git a/main/leds_task.c b/main/leds_task.c index 5b2eb2a7..1441fe8d 100644 --- a/main/leds_task.c +++ b/main/leds_task.c @@ -102,6 +102,11 @@ static void leds_main(void *ctx) struct leds_state *state = ctx; struct leds_stats *stats = &leds_stats[state->index]; + if (setup_leds(state)) { + LOG_ERROR("setup_leds"); + goto error; + } + for(struct stats_timer_sample loop_sample;; stats_timer_stop(&stats->loop, &loop_sample)) { EventBits_t event_bits = leds_task_wait(state); enum user_activity update_activity = 0; @@ -165,6 +170,9 @@ static void leds_main(void *ctx) } } } + +error: + return; } int init_leds_task(struct leds_state *state, const struct leds_config *config) From ed1dce444799f03905051f9a8060531782c19954 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 19:39:05 +0200 Subject: [PATCH 10/66] fixup! i2s_out: rename dma rx -> out --- components/i2s_out/esp32/dma.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 1c75d8d3..bb0df6bd 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -392,9 +392,6 @@ void i2s_out_dma_start(struct i2s_out *i2s_out) i2s_out->dma_end_desc->owner = 1; i2s_out->dma_end_desc->next = NULL; -#undef DEBUG -#define DEBUG 1 - for (unsigned i = 0; i < i2s_out->dma_out_count; i++) { LOG_DEBUG("dma_out_desc[%u]=%p: owner=%d eof=%d len=%u size=%u buf=%p next=%p", i, &i2s_out->dma_out_desc[i], From 20c7b5a380fda9279e37ed2288153d11806ec4fc Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 20:15:55 +0200 Subject: [PATCH 11/66] i2s_out: implement async start() mode --- components/i2s_out/esp32/dma.c | 221 ++++++++++++++++++--------- components/i2s_out/esp32/dma.h | 7 + components/i2s_out/esp32/i2s.c | 8 +- components/i2s_out/esp32/intr.c | 61 +++++++- components/i2s_out/i2s_out.c | 79 ++++++++-- components/i2s_out/i2s_out.h | 16 +- components/i2s_out/include/i2s_out.h | 6 +- 7 files changed, 307 insertions(+), 91 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index bb0df6bd..f0169999 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -65,41 +65,6 @@ void init_dma_desc(struct dma_desc *head, unsigned count, uint8_t *buf, size_t s } } -/* Prepare desc for DMA start */ -void init_dma_end_desc(struct dma_desc *eof_desc, uint32_t value, unsigned count) -{ - uint32_t *ptr = (uint32_t *) eof_desc->buf; - - for (unsigned i = 0; i < count; i++) { - ptr[i] = value; - } - - eof_desc->len = count * sizeof(value); -} - -void reinit_dma_desc(struct dma_desc *head, unsigned count, struct dma_desc *next) -{ - struct dma_desc **nextp = NULL; - - for (unsigned i = 0; i < count; i++) { - struct dma_desc *desc = &head[i]; - - if (nextp) { - *nextp = desc; - } - - desc->len = 0; - desc->owner = 0; - - nextp = &desc->next; - } - - if (nextp) { - // loop - *nextp = next; - } -} - int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align, unsigned repeat) { size_t buf_size = 0; @@ -165,6 +130,42 @@ int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align, unsigne return 0; } +/* Prepare end desc + buffer */ +void init_dma_end(struct dma_desc *eof_desc, uint32_t value, unsigned count) +{ + uint32_t *ptr = (uint32_t *) eof_desc->buf; + + for (unsigned i = 0; i < count; i++) { + ptr[i] = value; + } + + eof_desc->len = count * sizeof(value); + eof_desc->next = NULL; +} + +void reinit_dma_desc(struct dma_desc *head, unsigned count, struct dma_desc *next) +{ + struct dma_desc **nextp = NULL; + + for (unsigned i = 0; i < count; i++) { + struct dma_desc *desc = &head[i]; + + if (nextp) { + *nextp = desc; + } + + desc->len = 0; + desc->owner = 0; + + nextp = &desc->next; + } + + if (nextp) { + // loop + *nextp = next; + } +} + int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *options) { LOG_DEBUG("..."); @@ -173,12 +174,9 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt LOG_ERROR("eof_count=%u is too large for end buf size=%u", options->eof_count, DMA_END_BUF_SIZE); return -1; } - - // init EOF buffer - init_dma_end_desc(i2s_out->dma_end_desc, options->eof_value, options->eof_count); - - // init RX desc - reinit_dma_desc(i2s_out->dma_out_desc, i2s_out->dma_out_count, NULL); + // reinit out desc + init_dma_end(i2s_out->dma_end_desc, options->eof_value, options->eof_count); + reinit_dma_desc(i2s_out->dma_out_desc, i2s_out->dma_out_count, i2s_out->dma_end_desc); taskENTER_CRITICAL(&i2s_out->mux); @@ -193,20 +191,15 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt i2s_ll_rx_reset_dma(i2s_out->dev); i2s_ll_tx_reset_dma(i2s_out->dev); - i2s_ll_dma_enable_eof_on_fifo_empty(i2s_out->dev, true); - i2s_ll_dma_enable_owner_check(i2s_out->dev, true); - i2s_ll_dma_enable_auto_write_back(i2s_out->dev, true); - - i2s_ll_set_out_link_addr(i2s_out->dev, (uint32_t) i2s_out->dma_out_desc); - taskEXIT_CRITICAL(&i2s_out->mux); // reset eof state - xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF); + xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF); // reset write state i2s_out->dma_start = false; i2s_out->dma_write_desc = i2s_out->dma_out_desc; + i2s_out->dma_eof_desc = NULL; return 0; } @@ -214,10 +207,19 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt /* * Return pointer to current uncommitted dma_write_desc. */ -struct dma_desc *i2s_out_dma_desc(struct i2s_out *i2s_out) +struct dma_desc *i2s_out_dma_wait(struct i2s_out *i2s_out) { - if (i2s_out->dma_write_desc->owner) { - return NULL; + if (i2s_out->dma_start) { + while (!i2s_out->dma_eof_desc || i2s_out->dma_write_desc > i2s_out->dma_eof_desc) { + LOG_WARN("wait for dma_write_desc=%p > dma_eof_desc=%p", i2s_out->dma_write_desc, i2s_out->dma_eof_desc); + + xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, true, true, portMAX_DELAY); + } + } else { + if (i2s_out->dma_write_desc->owner) { + // last desc was already committed + return NULL; + } } return i2s_out->dma_write_desc; @@ -233,15 +235,15 @@ struct dma_desc *i2s_out_dma_next(struct i2s_out *i2s_out) i2s_out->dma_write_desc->owner = 1; } - if (!i2s_out->dma_write_desc->next) { - // no more descs left + if (i2s_out->dma_write_desc < i2s_out->dma_out_desc + i2s_out->dma_out_count) { + // start using next desc + i2s_out->dma_write_desc++; + } else { + // no more descs available return NULL; } - // start using next desc - i2s_out->dma_write_desc = i2s_out->dma_write_desc->next; - - return i2s_out->dma_write_desc; + return i2s_out_dma_wait(i2s_out); } /* @@ -254,7 +256,7 @@ size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, s struct dma_desc *desc; // stop if last desc already committed - if (!(desc = i2s_out_dma_desc(i2s_out))) { + if (!(desc = i2s_out_dma_wait(i2s_out))) { LOG_WARN("owned desc=%p (owner=%u eof=%u buf=%p len=%u size=%u)", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size); // unable to find a usable DMA buffer, TX buffers full @@ -330,12 +332,30 @@ int i2s_out_dma_write(struct i2s_out *i2s_out, const void *data, size_t size) return len; } -void i2s_out_dma_repeat(struct i2s_out *i2s_out, unsigned count) +int i2s_out_dma_repeat(struct i2s_out *i2s_out, unsigned count) { struct dma_desc **nextp = &i2s_out->dma_write_desc->next; + if (i2s_out->dma_start) { + LOG_ERROR("dma_start=%u", i2s_out->dma_start); + return -1; + } // commit - i2s_out->dma_write_desc->owner = 1; + if (!i2s_out->dma_write_desc->owner) { + LOG_DEBUG("commit desc=%p: owner=%u eof=%u buf=%p len=%u size=%u next=%p", + i2s_out->dma_write_desc, + i2s_out->dma_write_desc->owner, + i2s_out->dma_write_desc->eof, + i2s_out->dma_write_desc->buf, + i2s_out->dma_write_desc->len, + i2s_out->dma_write_desc->size, + i2s_out->dma_write_desc->next + ); + + i2s_out->dma_write_desc->owner = 1; + } + + LOG_DEBUG("count=%u", count); for (unsigned i = 0; i < count; i++) { for (unsigned j = 0; j < i2s_out->dma_out_count; j++) { @@ -361,27 +381,49 @@ void i2s_out_dma_repeat(struct i2s_out *i2s_out, unsigned count) // eof *nextp = i2s_out->dma_end_desc; } + + return 0; } int i2s_out_dma_pending(struct i2s_out *i2s_out) { - if (i2s_out->dma_start) { - // start() already haṕpened - return 0; + if (i2s_out->dma_write_desc > i2s_out->dma_out_desc || i2s_out->dma_write_desc->len > 0) { + // write() happened + return 1; } - if (i2s_out->dma_write_desc != i2s_out->dma_out_desc || i2s_out->dma_write_desc->len > 0) { - // write() happened + return 0; +} + +int i2s_out_dma_running(struct i2s_out *i2s_out) +{ + if (i2s_out->dma_start) { + // start() has been called, flush() has not return 1; } return 0; } -void i2s_out_dma_start(struct i2s_out *i2s_out) +int i2s_out_dma_start(struct i2s_out *i2s_out) { + if (i2s_out->dma_start) { + LOG_ERROR("dma_start=%u", i2s_out->dma_start); + return -1; + } + // commit if not repeat() if (!i2s_out->dma_write_desc->owner) { + LOG_DEBUG("commit desc=%p: owner=%u eof=%u buf=%p len=%u size=%u next=%p", + i2s_out->dma_write_desc, + i2s_out->dma_write_desc->owner, + i2s_out->dma_write_desc->eof, + i2s_out->dma_write_desc->buf, + i2s_out->dma_write_desc->len, + i2s_out->dma_write_desc->size, + i2s_out->dma_write_desc->next + ); + i2s_out->dma_write_desc->owner = 1; } @@ -428,12 +470,23 @@ void i2s_out_dma_start(struct i2s_out *i2s_out) i2s_out->dma_end_desc->next ); + // reset eof state + xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF); + + i2s_out->dma_eof_desc = NULL; + taskENTER_CRITICAL(&i2s_out->mux); i2s_ll_tx_reset_dma(i2s_out->dev); i2s_ll_tx_reset_fifo(i2s_out->dev); i2s_ll_tx_reset(i2s_out->dev); + i2s_ll_set_out_link_addr(i2s_out->dev, (uint32_t) i2s_out->dma_out_desc); + + i2s_ll_dma_enable_eof_on_fifo_empty(i2s_out->dev, true); + i2s_ll_dma_enable_owner_check(i2s_out->dev, true); + i2s_ll_dma_enable_auto_write_back(i2s_out->dev, false); + i2s_intr_clear(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_CLR | I2S_OUT_DSCR_ERR_INT_CLR | I2S_OUT_EOF_INT_CLR); i2s_intr_enable(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_ENA | I2S_OUT_DSCR_ERR_INT_ENA | I2S_OUT_EOF_INT_ENA); @@ -442,20 +495,52 @@ void i2s_out_dma_start(struct i2s_out *i2s_out) taskEXIT_CRITICAL(&i2s_out->mux); + // reset state for next write() i2s_out->dma_start = true; + i2s_out->dma_write_desc = i2s_out->dma_out_desc; + + return 0; } int i2s_out_dma_flush(struct i2s_out *i2s_out) { - LOG_DEBUG("wait event_group bits=%08x", I2S_OUT_EVENT_GROUP_BIT_DMA_EOF); + LOG_DEBUG("wait event_group bits=%08x...", I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF); - xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, false, false, portMAX_DELAY); + xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF, false, false, portMAX_DELAY); LOG_DEBUG("wait done"); return 0; } +void i2s_out_dma_stop(struct i2s_out *i2s_out) +{ + LOG_DEBUG(""); + + // reset write state + i2s_out->dma_start = false; + + taskENTER_CRITICAL(&i2s_out->mux); + + i2s_intr_disable(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_ENA | I2S_OUT_DSCR_ERR_INT_ENA | I2S_OUT_EOF_INT_ENA); + i2s_intr_clear(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_CLR | I2S_OUT_DSCR_ERR_INT_CLR | I2S_OUT_EOF_INT_CLR); + + i2s_ll_rx_stop_link(i2s_out->dev); + i2s_ll_tx_stop_link(i2s_out->dev); + + i2s_ll_enable_dma(i2s_out->dev, false); + + i2s_ll_rx_reset_dma(i2s_out->dev); + i2s_ll_tx_reset_dma(i2s_out->dev); + + taskEXIT_CRITICAL(&i2s_out->mux); + + // reset eof state + xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF); + + i2s_out->dma_eof_desc = NULL; +} + void i2s_out_dma_free(struct i2s_out *i2s_out) { free(i2s_out->dma_end_buf); diff --git a/components/i2s_out/esp32/dma.h b/components/i2s_out/esp32/dma.h index 38a00d77..87da96e3 100644 --- a/components/i2s_out/esp32/dma.h +++ b/components/i2s_out/esp32/dma.h @@ -20,6 +20,13 @@ struct dma_desc { struct dma_desc *next; }; +/* Prepare desc for re-use after DMA EOF */ +static inline void i2s_dma_desc_reset(struct dma_desc *eof_desc) +{ + eof_desc->len = 0; + eof_desc->owner = 0; +} + static inline void i2s_dma_tx_get_des_addr(i2s_dev_t *hw, uint32_t *dscr_addr) { *dscr_addr = hw->out_link_dscr; diff --git a/components/i2s_out/esp32/i2s.c b/components/i2s_out/esp32/i2s.c index 45a79fac..a19940b0 100644 --- a/components/i2s_out/esp32/i2s.c +++ b/components/i2s_out/esp32/i2s.c @@ -116,7 +116,7 @@ int i2s_out_i2s_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt i2s_out->dev->sample_rate_conf.val ); - // reset eof state + // reset event state xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF); return 0; @@ -126,6 +126,9 @@ void i2s_out_i2s_start(struct i2s_out *i2s_out) { LOG_DEBUG(""); + // reset event state + xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF); + taskENTER_CRITICAL(&i2s_out->mux); // NOTE: there seems to always be three extra BCK cycles at the start of TX @@ -169,4 +172,7 @@ void i2s_out_i2s_stop(struct i2s_out *i2s_out) i2s_ll_tx_stop(i2s_out->dev); taskEXIT_CRITICAL(&i2s_out->mux); + + // reset event state + xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF); } diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index 82e92caa..8c3de6dc 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -25,12 +25,8 @@ void IRAM_ATTR i2s_intr_out_total_eof_handler(struct i2s_out *i2s_out, BaseType_ LOG_ISR_DEBUG("desc=%p", eof_addr); - // NOTE: this is unlikely to stop DMA before this repeats at least once - i2s_ll_tx_stop_link(i2s_out->dev); - i2s_ll_rx_stop_link(i2s_out->dev); - - // unblock flush() task - xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, task_wokenp); + // unblock flush() tasks + xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF, task_wokenp); i2s_intr_clear(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_CLR); } @@ -54,7 +50,58 @@ void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *tas struct dma_desc *eof_desc = (struct dma_desc *) eof_addr; - LOG_ISR_DEBUG("desc=%p owner=%u", eof_desc, eof_desc->owner); + // only handle normal out_desc, not repeat_desc or end_desc + if (eof_desc >= i2s_out->dma_out_desc && eof_desc < i2s_out->dma_out_desc + i2s_out->dma_out_count) { + LOG_ISR_DEBUG("eof desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); + + // speculation: we may miss sone EOF ISRs + if (i2s_out->dma_eof_desc) { + for (struct dma_desc *desc = eof_desc; desc > i2s_out->dma_eof_desc; desc--) { + if (desc != eof_desc) { + LOG_ISR_WARN("miss desc=%p owner=%u len=%u", desc, desc->owner, desc->len); + } + + i2s_dma_desc_reset(desc); + } + } else { + i2s_dma_desc_reset(eof_desc); + } + + i2s_out->dma_eof_desc = eof_desc; + + // unblock get() task + xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, task_wokenp); + + } else if (eof_desc == i2s_out->dma_end_desc) { + + // speculation: we might miss EOF ISR for an intermediate DMA descriptor, and hit TOTAL_EOF with eof_addr at the end_desc + // resulting in wait() deadlocking on dma_eof_desc not being updated + eof_desc = i2s_out->dma_out_desc + i2s_out->dma_out_count - 1; + + if (!i2s_out->dma_eof_desc) { + LOG_ISR_WARN("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); + + for (struct dma_desc *desc = eof_desc; desc >= i2s_out->dma_out_desc; desc--) { + i2s_dma_desc_reset(desc); + } + } else if (i2s_out->dma_eof_desc != eof_desc) { + LOG_ISR_WARN("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); + + for (struct dma_desc *desc = eof_desc; desc > i2s_out->dma_eof_desc; desc--) { + i2s_dma_desc_reset(desc); + } + } else { + LOG_ISR_DEBUG("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); + } + + i2s_out->dma_eof_desc = eof_desc; + + // unblock get() task + xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, task_wokenp); + + } else { + LOG_ISR_DEBUG("ignore desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); + } i2s_intr_clear(i2s_out->dev, I2S_OUT_EOF_INT_CLR); } diff --git a/components/i2s_out/i2s_out.c b/components/i2s_out/i2s_out.c index a93f4d95..c9b0e173 100644 --- a/components/i2s_out/i2s_out.c +++ b/components/i2s_out/i2s_out.c @@ -335,7 +335,10 @@ int i2s_out_repeat(struct i2s_out *i2s_out, unsigned count) return -1; } - i2s_out_dma_repeat(i2s_out, count); + if ((err = i2s_out_dma_repeat(i2s_out, count))) { + LOG_ERROR("i2s_out_dma_repeat"); + return err; + } if (!xSemaphoreGiveRecursive(i2s_out->mutex)) { LOG_WARN("xSemaphoreGiveRecursive"); @@ -344,7 +347,7 @@ int i2s_out_repeat(struct i2s_out *i2s_out, unsigned count) return err; } -int i2s_out_flush(struct i2s_out *i2s_out) +int i2s_out_wait(struct i2s_out *i2s_out) { int err = 0; @@ -353,20 +356,77 @@ int i2s_out_flush(struct i2s_out *i2s_out) return -1; } + // wait for previous start() to complete? + if (i2s_out_dma_running(i2s_out)) { + if ((err = i2s_out_dma_flush(i2s_out))) { + LOG_ERROR("i2s_out_dma_flush"); + goto error; + } + + if ((err = i2s_out_i2s_flush(i2s_out))) { + LOG_ERROR("i2s_out_i2s_flush"); + goto error; + } + + i2s_out_dma_stop(i2s_out); + i2s_out_i2s_stop(i2s_out); + } + +error: + if (!xSemaphoreGiveRecursive(i2s_out->mutex)) { + LOG_WARN("xSemaphoreGiveRecursive"); + } + + return err; + +} + +int i2s_out_start(struct i2s_out *i2s_out) +{ + int err = 0; + + if (!xSemaphoreTakeRecursive(i2s_out->mutex, portMAX_DELAY)) { + LOG_ERROR("xSemaphoreTakeRecursive"); + return -1; + } + + // wait for previous start() to complete? + if ((err = i2s_out_wait(i2s_out))) { + goto error; + } + + // have write()? if (i2s_out_dma_pending(i2s_out)) { - i2s_out_dma_start(i2s_out); + if ((err = i2s_out_dma_start(i2s_out))) { + LOG_ERROR("i2s_out_dma_start"); + return err; + } + i2s_out_i2s_start(i2s_out); } - // wait for TX DMA EOF - if ((err = i2s_out_dma_flush(i2s_out))) { - LOG_ERROR("i2s_out_dma_flush"); +error: + if (!xSemaphoreGiveRecursive(i2s_out->mutex)) { + LOG_WARN("xSemaphoreGiveRecursive"); + } + + return err; +} + +int i2s_out_flush(struct i2s_out *i2s_out) +{ + int err = 0; + + if (!xSemaphoreTakeRecursive(i2s_out->mutex, portMAX_DELAY)) { + LOG_ERROR("xSemaphoreTakeRecursive"); + return -1; + } + + if ((err = i2s_out_start(i2s_out))) { goto error; } - // wait for I2S TX done - if ((err = i2s_out_i2s_flush(i2s_out))) { - LOG_ERROR("i2s_out_i2s_flush"); + if ((err = i2s_out_wait(i2s_out))) { goto error; } @@ -382,6 +442,7 @@ int i2s_out_close(struct i2s_out *i2s_out) { int err = i2s_out_flush(i2s_out); + i2s_out_dma_stop(i2s_out); i2s_out_i2s_stop(i2s_out); i2s_out_pin_teardown(i2s_out); diff --git a/components/i2s_out/i2s_out.h b/components/i2s_out/i2s_out.h index 7298bbbc..8361e19a 100644 --- a/components/i2s_out/i2s_out.h +++ b/components/i2s_out/i2s_out.h @@ -12,8 +12,9 @@ struct dma_desc; -#define I2S_OUT_EVENT_GROUP_BIT_DMA_EOF (1 << 0) -#define I2S_OUT_EVENT_GROUP_BIT_I2S_EOF (1 << 1) +#define I2S_OUT_EVENT_GROUP_BIT_DMA_EOF (1 << 0) +#define I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF (1 << 1) +#define I2S_OUT_EVENT_GROUP_BIT_I2S_EOF (1 << 2) struct i2s_out { i2s_port_t port; @@ -50,7 +51,10 @@ struct i2s_out { // pointer to software-owned dma_out_desc used for write() struct dma_desc *dma_write_desc; - bool dma_start; // set by i2s_out_dma_start + // pointer to previous hardware-owned dma_out_desc, now available for write() - updated by ISR + struct dma_desc *dma_eof_desc; + + bool dma_start; // initialized by i2s_out_dma_setup(), set by i2s_out_dma_start(), cleared by i2s_out_dma_stop() }; /* dma.c */ @@ -59,10 +63,12 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, size_t size); void i2s_out_dma_commit(struct i2s_out *i2s_out, unsigned count, size_t size); int i2s_out_dma_write(struct i2s_out *i2s_out, const void *data, size_t size); -void i2s_out_dma_repeat(struct i2s_out *i2s_out, unsigned count); +int i2s_out_dma_repeat(struct i2s_out *i2s_out, unsigned count); +int i2s_out_dma_running(struct i2s_out *i2s_out); int i2s_out_dma_pending(struct i2s_out *i2s_out); -void i2s_out_dma_start(struct i2s_out *i2s_out); +int i2s_out_dma_start(struct i2s_out *i2s_out); int i2s_out_dma_flush(struct i2s_out *i2s_out); +void i2s_out_dma_stop(struct i2s_out *i2s_out); void i2s_out_dma_free(struct i2s_out *i2s_out); /* i2s.c */ diff --git a/components/i2s_out/include/i2s_out.h b/components/i2s_out/include/i2s_out.h index db4fac7f..50dd5127 100644 --- a/components/i2s_out/include/i2s_out.h +++ b/components/i2s_out/include/i2s_out.h @@ -220,7 +220,11 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t #endif /** - * Setup DMA to repeat all written buffers given count of times. Completes any writes. + * Setup DMA to repeat all written buffers given count of times. + * + * Can only be called between open() -> write() and flush() -> close() in sync mode. + * Cannot be called after start(), and cannot call write() after. + * Remains in effect until next close() and open(). * * Returns <0 on error, 0 on success. */ From a1231cb05233527522b6444912e5ed85e8ca82a5 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 20:16:12 +0200 Subject: [PATCH 12/66] main: fix i2s_data_copies ordering --- main/leds_configtab.i | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/main/leds_configtab.i b/main/leds_configtab.i index 0efed4d5..e6568017 100644 --- a/main/leds_configtab.i +++ b/main/leds_configtab.i @@ -77,15 +77,6 @@ const struct configtab LEDS_CONFIGTAB[] = { .description = "Output I2S bit rate. Only used for protocols with a separate clock/data.", .enum_type = { .value = &LEDS_CONFIG.i2s_clock, .values = leds_i2s_clock_enum, .default_value = I2S_CLOCK_DEFAULT }, }, - { CONFIG_TYPE_UINT16, "i2s_data_copies", - .description = ( - "Output multiple copies of the data on each I2S output, to control a series of identical LEDs.\n" - "\tFor example, use count = 600, i2s_data_width = 4, i2s_data_copies = 4 to control 4 sets of 150 LEDs on each of four outputs." - "Each output will be independently controllable, but each set of 150 LEDs per output will behave identically.\n" - "\t0 -> disabled, 1 -> no effect, N -> repeat data for a total of N copies per output." - ), - .uint16_type = { .value = &LEDS_CONFIG.i2s_data_copies, .max = LEDS_I2S_REPEAT_MAX }, - }, # if LEDS_I2S_PARALLEL_ENABLED { CONFIG_TYPE_UINT16, "i2s_data_width", .description = ( @@ -99,6 +90,15 @@ const struct configtab LEDS_CONFIGTAB[] = { .ctx = &LEDS_CONFIG, }, # endif + { CONFIG_TYPE_UINT16, "i2s_data_copies", + .description = ( + "Output multiple copies of the data on each I2S output, to control a series of identical LEDs.\n" + "\tFor example, use count = 600, i2s_data_width = 4, i2s_data_copies = 4 to control 4 sets of 150 LEDs on each of four outputs." + "Each output will be independently controllable, but each set of 150 LEDs per output will behave identically.\n" + "\t0 -> disabled, 1 -> no effect, N -> repeat data for a total of N copies per output." + ), + .uint16_type = { .value = &LEDS_CONFIG.i2s_data_copies, .max = LEDS_I2S_REPEAT_MAX }, + }, # if LEDS_I2S_GPIO_PINS_ENABLED { CONFIG_TYPE_UINT16, "i2s_clock_pin", .description = "Output I2S bit clock to GPIO pin. Only used for protocols with a separate clock/data.", From 8481445ec53d0287874d8772234689071f9f8440 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 20:18:34 +0200 Subject: [PATCH 13/66] main: USER_ALERT_ERROR_LEDS on leds_setup() errors --- main/leds_task.c | 5 ++++- main/user.c | 3 ++- main/user.h | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/main/leds_task.c b/main/leds_task.c index 1441fe8d..cac7cdb9 100644 --- a/main/leds_task.c +++ b/main/leds_task.c @@ -172,7 +172,10 @@ static void leds_main(void *ctx) } error: - return; + user_alert(USER_ALERT_ERROR_LEDS); + LOG_ERROR("task=%p stopped", state->task); + state->task = NULL; + vTaskDelete(NULL); } int init_leds_task(struct leds_state *state, const struct leds_config *config) diff --git a/main/user.c b/main/user.c index 3bc6f944..232bfb97 100644 --- a/main/user.c +++ b/main/user.c @@ -67,7 +67,8 @@ const char *user_alert_str(enum user_alert alert) case USER_ALERT_ERROR_WIFI: return "ERROR_WIFI"; case USER_ALERT_ERROR_START: return "ERROR_START"; case USER_ALERT_ERROR_DMX: return "ERROR_DMX"; - + + case USER_ALERT_ERROR_LEDS: return "ERROR_LEDS"; case USER_ALERT_ERROR_LEDS_SEQUENCE: return "ERROR_LEDS_SEQUENCE"; case USER_ALERT_ERROR_LEDS_SEQUENCE_READ: return "ERROR_LEDS_SEQUENCE_READ"; case USER_ALERT_ERROR_ATX_PSU_TIMEOUT: return "ERROR_ATX_PSU_TIMEOUT"; diff --git a/main/user.h b/main/user.h index 511d5d66..d4d115f1 100644 --- a/main/user.h +++ b/main/user.h @@ -48,6 +48,7 @@ enum user_alert { USER_ALERT_ERROR_WIFI, USER_ALERT_ERROR_START, USER_ALERT_ERROR_DMX, + USER_ALERT_ERROR_LEDS, USER_ALERT_ERROR_LEDS_SEQUENCE, USER_ALERT_ERROR_LEDS_SEQUENCE_READ, USER_ALERT_ERROR_ATX_PSU_TIMEOUT, From 91928583f4b59dfec21d5737804bfd9ec5a2b8eb Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 20:18:49 +0200 Subject: [PATCH 14/66] logging: optional ISR WARN --- components/logging/include/logging.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/components/logging/include/logging.h b/components/logging/include/logging.h index 9dc941ad..5ed01844 100644 --- a/components/logging/include/logging.h +++ b/components/logging/include/logging.h @@ -20,14 +20,20 @@ #define DEBUG 0 #endif +#ifdef WARN + #define ISR_WARN 1 +#else + #define ISR_WARN 0 +#endif + #if CONFIG_LOG_COLORS #undef LOG_COLOR_D #define LOG_COLOR_D LOG_COLOR(LOG_COLOR_BLUE) #endif /* Bypass stdio buffering/interrupts, blocking write directly to UART */ -#define LOG_ISR_ERROR(fmt, ...) do { if (DEBUG) esp_rom_printf(LOG_FORMAT(E, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) -#define LOG_ISR_WARN(fmt, ...) do { if (DEBUG) esp_rom_printf(LOG_FORMAT(W, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) +#define LOG_ISR_ERROR(fmt, ...) do { if (ISR_WARN) esp_rom_printf(LOG_FORMAT(E, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) +#define LOG_ISR_WARN(fmt, ...) do { if (ISR_WARN) esp_rom_printf(LOG_FORMAT(W, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) #define LOG_ISR_INFO(fmt, ...) do { if (DEBUG) esp_rom_printf(LOG_FORMAT(I, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) #define LOG_ISR_DEBUG(fmt, ...) do { if (DEBUG) esp_rom_printf(LOG_FORMAT(D, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) From f2b20aadaac689267d4f0240aaaad3f17d0fb074 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 20:25:17 +0200 Subject: [PATCH 15/66] i2s_out: cleanup i2s_out_intr_setup() DEBUG logging --- components/i2s_out/esp32/intr.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index 8c3de6dc..78bef578 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -154,21 +154,22 @@ int i2s_out_intr_setup(struct i2s_out *i2s_out, const struct i2s_out_options *op { esp_err_t err = 0; - LOG_DEBUG("intr=%p", i2s_out->intr); + if (i2s_out->intr) { + return 0; + } taskENTER_CRITICAL(&i2s_out->mux); i2s_intr_disable_all(i2s_out->dev); - - if (!i2s_out->intr) { - err = esp_intr_alloc(i2s_irq[i2s_out->port], I2S_INTR_ALLOC_FLAGS, i2s_intr_handler, i2s_out, &i2s_out->intr); - } + err = esp_intr_alloc(i2s_irq[i2s_out->port], I2S_INTR_ALLOC_FLAGS, i2s_intr_handler, i2s_out, &i2s_out->intr); taskEXIT_CRITICAL(&i2s_out->mux); if (err) { LOG_ERROR("esp_intr_alloc: %s", esp_err_to_name(err)); return -1; + } else { + LOG_DEBUG("intr=%p", i2s_out->intr); } return 0; From 6e955e7fac87128d9916f81ebf3547925a73fd13 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 20:28:11 +0200 Subject: [PATCH 16/66] i2s_out: i2s_out_dma_wait() -> DEBUG --- components/i2s_out/esp32/dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index f0169999..c0429dbc 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -211,7 +211,7 @@ struct dma_desc *i2s_out_dma_wait(struct i2s_out *i2s_out) { if (i2s_out->dma_start) { while (!i2s_out->dma_eof_desc || i2s_out->dma_write_desc > i2s_out->dma_eof_desc) { - LOG_WARN("wait for dma_write_desc=%p > dma_eof_desc=%p", i2s_out->dma_write_desc, i2s_out->dma_eof_desc); + LOG_DEBUG("wait for dma_write_desc=%p > dma_eof_desc=%p", i2s_out->dma_write_desc, i2s_out->dma_eof_desc); xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, true, true, portMAX_DELAY); } From 0adf94e959d5a3a3b7b89b80aaf4253412819e04 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 20:54:09 +0200 Subject: [PATCH 17/66] logging: extra TRACE level --- components/logging/include/logging.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/components/logging/include/logging.h b/components/logging/include/logging.h index 5ed01844..f73233f2 100644 --- a/components/logging/include/logging.h +++ b/components/logging/include/logging.h @@ -13,6 +13,13 @@ #include #include +#ifdef TRACE + #undef TRACE + #define TRACE 1 +#else + #define TRACE 0 +#endif + #ifdef DEBUG #undef DEBUG #define DEBUG 1 @@ -49,11 +56,9 @@ #define LOG_WARN(fmt, ...) do { fprintf(stderr, LOG_FORMAT(W, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) #define LOG_INFO(fmt, ...) do { fprintf(stderr, LOG_FORMAT(I, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) #define LOG_DEBUG(fmt, ...) do { if (DEBUG) fprintf(stderr, LOG_FORMAT(D, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) +#define LOG_TRACE(fmt, ...) do { if (TRACE) fprintf(stderr, LOG_FORMAT(D, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) -// XXX: CONFIG_LOG_DEFAULT_LEVEL defaults to skip ESP_LOG_DEBUG, and raising will include ALL debug output by default - not possible to override this per-call -#if DEBUG - #define LOG_DEBUG_BUFFER(buf, len) do { if (DEBUG) ESP_LOG_BUFFER_HEX_LEVEL(__func__, buf, len, ESP_LOG_INFO); } while(0) -#else - #define LOG_DEBUG_BUFFER(buf, len) -#endif +// XXX: use ESP_LOG_INFO because CONFIG_LOG_DEFAULT_LEVEL defaults to skip ESP_LOG_DEBUG, and raising will include ALL debug output by default - not possible to override this per-call #define LOG_INFO_BUFFER(buf, len) ESP_LOG_BUFFER_HEX_LEVEL(__func__, buf, len, ESP_LOG_INFO); +#define LOG_DEBUG_BUFFER(buf, len) do { if (DEBUG) ESP_LOG_BUFFER_HEX_LEVEL(__func__, buf, len, ESP_LOG_INFO); } while(0) +#define LOG_TRACE_BUFFER(buf, len) do { if (TRACE) ESP_LOG_BUFFER_HEX_LEVEL(__func__, buf, len, ESP_LOG_INFO); } while(0) From 07397a4f9f8bf590412807a1211ea036e2b95ebb Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 20:54:47 +0200 Subject: [PATCH 18/66] i2s_out: dma TRACE logging --- components/i2s_out/esp32/dma.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index c0429dbc..c9fa4f7f 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -293,13 +293,13 @@ size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, s // limit to available buffer size count = (desc->size - desc->len) / size; - LOG_DEBUG("short desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) -> count=%u size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, count, size); + LOG_TRACE("short desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) -> count=%u size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, count, size); } else if (desc->len + count * size < desc->size) { - LOG_DEBUG("long desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) -> count=%u size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, count, size); + LOG_TRACE("long desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) -> count=%u size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, count, size); } else { - LOG_DEBUG("fill desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) -> count=%u size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, count, size); + LOG_TRACE("fill desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) -> count=%u size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, count, size); } *ptr = desc->buf + desc->len; @@ -321,8 +321,8 @@ int i2s_out_dma_write(struct i2s_out *i2s_out, const void *data, size_t size) if (len) { // copy data to desc buf - LOG_DEBUG("copy len=%u -> ptr=%p", len, ptr); - LOG_DEBUG_BUFFER(data, len); + LOG_TRACE("copy len=%u -> ptr=%p", len, ptr); + LOG_TRACE_BUFFER(data, len); memcpy(ptr, data, len); From 13c510f99252b6c00567931c92e6fd3f54f46e3c Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 20:58:10 +0200 Subject: [PATCH 19/66] leds: bump TEST_FRAME_RATE -> 30 --- components/leds/test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/leds/test.c b/components/leds/test.c index 1c3db83a..60d521c3 100644 --- a/components/leds/test.c +++ b/components/leds/test.c @@ -8,7 +8,7 @@ #include -#define TEST_FRAME_RATE (25) +#define TEST_FRAME_RATE (30) #define TEST_FRAME_TICKS (1000 / TEST_FRAME_RATE / portTICK_RATE_MS) #define TEST_MODE_COLOR_FRAMES 25 From be8e43b3e172c2909064bf96a2d972f276d6e6a0 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 21:24:50 +0200 Subject: [PATCH 20/66] i2s_out: fix goto error -> locking --- components/i2s_out/i2s_out.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/i2s_out/i2s_out.c b/components/i2s_out/i2s_out.c index c9b0e173..2e8796cf 100644 --- a/components/i2s_out/i2s_out.c +++ b/components/i2s_out/i2s_out.c @@ -337,13 +337,14 @@ int i2s_out_repeat(struct i2s_out *i2s_out, unsigned count) if ((err = i2s_out_dma_repeat(i2s_out, count))) { LOG_ERROR("i2s_out_dma_repeat"); - return err; + goto error; } if (!xSemaphoreGiveRecursive(i2s_out->mutex)) { LOG_WARN("xSemaphoreGiveRecursive"); } +error: return err; } @@ -399,7 +400,7 @@ int i2s_out_start(struct i2s_out *i2s_out) if (i2s_out_dma_pending(i2s_out)) { if ((err = i2s_out_dma_start(i2s_out))) { LOG_ERROR("i2s_out_dma_start"); - return err; + goto error; } i2s_out_i2s_start(i2s_out); From 0b0e52c0816558fc15fdb73c06f4d22d358d3904 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 21:25:09 +0200 Subject: [PATCH 21/66] i2s_out: timeouts --- components/i2s_out/esp32/dma.c | 44 +++++++++++------- components/i2s_out/esp32/i2s.c | 13 ++++-- components/i2s_out/i2s_out.c | 64 +++++++++++++------------- components/i2s_out/i2s_out.h | 8 ++-- components/i2s_out/include/i2s_out.h | 18 ++++---- components/leds/include/leds.h | 3 ++ components/leds/interfaces/i2s.h | 1 + components/leds/interfaces/i2s/setup.c | 5 +- components/leds/interfaces/i2s/tx.c | 20 ++++---- main/leds_i2s.c | 4 ++ 10 files changed, 103 insertions(+), 77 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index c9fa4f7f..544e79e0 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -207,13 +207,18 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt /* * Return pointer to current uncommitted dma_write_desc. */ -struct dma_desc *i2s_out_dma_wait(struct i2s_out *i2s_out) +struct dma_desc *i2s_out_dma_wait(struct i2s_out *i2s_out, TickType_t timeout) { if (i2s_out->dma_start) { while (!i2s_out->dma_eof_desc || i2s_out->dma_write_desc > i2s_out->dma_eof_desc) { LOG_DEBUG("wait for dma_write_desc=%p > dma_eof_desc=%p", i2s_out->dma_write_desc, i2s_out->dma_eof_desc); - xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, true, true, portMAX_DELAY); + EventBits_t bits = xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, true, true, timeout); + + if (!(bits & I2S_OUT_EVENT_GROUP_BIT_DMA_EOF)) { + LOG_WARN("timeout -> bits=%08x", bits); + return NULL; + } } } else { if (i2s_out->dma_write_desc->owner) { @@ -228,7 +233,7 @@ struct dma_desc *i2s_out_dma_wait(struct i2s_out *i2s_out) /* * Commit current dma_write_desc and return next. */ -struct dma_desc *i2s_out_dma_next(struct i2s_out *i2s_out) +struct dma_desc *i2s_out_dma_next(struct i2s_out *i2s_out, TickType_t timeout) { if (!i2s_out->dma_write_desc->owner) { // prepare for DMA @@ -243,7 +248,7 @@ struct dma_desc *i2s_out_dma_next(struct i2s_out *i2s_out) return NULL; } - return i2s_out_dma_wait(i2s_out); + return i2s_out_dma_wait(i2s_out, timeout); } /* @@ -251,15 +256,15 @@ struct dma_desc *i2s_out_dma_next(struct i2s_out *i2s_out) * * @return size of usable buffer in units of size, up to count. 0 if full */ -size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, size_t size) +size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, size_t size, TickType_t timeout) { struct dma_desc *desc; // stop if last desc already committed - if (!(desc = i2s_out_dma_wait(i2s_out))) { - LOG_WARN("owned desc=%p (owner=%u eof=%u buf=%p len=%u size=%u)", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size); + if (!(desc = i2s_out_dma_wait(i2s_out, timeout))) { + LOG_WARN("i2s_out_dma_wait"); - // unable to find a usable DMA buffer, TX buffers full + // unable to find a usable DMA buffer, timeout or TX buffers full? *ptr = NULL; return 0; @@ -270,13 +275,13 @@ size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, s LOG_DEBUG("commit desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) < size=%u -> next=%p", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, size, desc->next); // commit, try with the next desc, if available - desc = i2s_out_dma_next(i2s_out); + desc = i2s_out_dma_next(i2s_out, timeout); } if (!desc) { - LOG_WARN("last desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) < size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, size); + LOG_WARN("i2s_out_dma_next"); - // unable to find a usable DMA buffer, TX buffers full + // unable to find a usable DMA buffer, timeout or TX buffers full? *ptr = NULL; return 0; @@ -314,10 +319,10 @@ void i2s_out_dma_commit(struct i2s_out *i2s_out, unsigned count, size_t size) desc->len += count * size; } -int i2s_out_dma_write(struct i2s_out *i2s_out, const void *data, size_t size) +int i2s_out_dma_write(struct i2s_out *i2s_out, const void *data, size_t size, TickType_t timeout) { void *ptr; - int len = i2s_out_dma_buffer(i2s_out, &ptr, size, 1); // single unaligned bytes + int len = i2s_out_dma_buffer(i2s_out, &ptr, size, 1, timeout); // single unaligned bytes if (len) { // copy data to desc buf @@ -502,13 +507,18 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) return 0; } -int i2s_out_dma_flush(struct i2s_out *i2s_out) +int i2s_out_dma_flush(struct i2s_out *i2s_out, TickType_t timeout) { - LOG_DEBUG("wait event_group bits=%08x...", I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF); + LOG_DEBUG("..."); - xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF, false, false, portMAX_DELAY); + EventBits_t bits = xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF, false, false, timeout); - LOG_DEBUG("wait done"); + if (!(bits & I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF)) { + LOG_ERROR("timeout -> bits=%08x", bits); + return -1; + } else { + LOG_DEBUG("wait -> bits=%08x", bits); + } return 0; } diff --git a/components/i2s_out/esp32/i2s.c b/components/i2s_out/esp32/i2s.c index a19940b0..7193b2ff 100644 --- a/components/i2s_out/esp32/i2s.c +++ b/components/i2s_out/esp32/i2s.c @@ -140,7 +140,7 @@ void i2s_out_i2s_start(struct i2s_out *i2s_out) taskEXIT_CRITICAL(&i2s_out->mux); } -int i2s_out_i2s_flush(struct i2s_out *i2s_out) +int i2s_out_i2s_flush(struct i2s_out *i2s_out, TickType_t timeout) { EventBits_t bits; @@ -155,9 +155,16 @@ int i2s_out_i2s_flush(struct i2s_out *i2s_out) taskEXIT_CRITICAL(&i2s_out->mux); - LOG_DEBUG("wait event_group bits=%08x", I2S_OUT_EVENT_GROUP_BIT_I2S_EOF); + LOG_DEBUG("..."); - xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF, false, false, portMAX_DELAY); + bits = xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF, false, false, timeout); + + if (!(bits & I2S_OUT_EVENT_GROUP_BIT_I2S_EOF)) { + LOG_ERROR("timeout -> bits=%08x", bits); + return -1; + } else { + LOG_DEBUG("wait -> bits=%08x", bits); + } } return 0; diff --git a/components/i2s_out/i2s_out.c b/components/i2s_out/i2s_out.c index 2e8796cf..74eb46f4 100644 --- a/components/i2s_out/i2s_out.c +++ b/components/i2s_out/i2s_out.c @@ -74,11 +74,11 @@ int i2s_out_new(struct i2s_out **i2s_outp, i2s_port_t port, size_t buffer_size, return err; } -int i2s_out_open(struct i2s_out *i2s_out, const struct i2s_out_options *options) +int i2s_out_open(struct i2s_out *i2s_out, const struct i2s_out_options *options, TickType_t timeout) { int err = 0; - if (!xSemaphoreTakeRecursive(i2s_out->mutex, portMAX_DELAY)) { + if (!xSemaphoreTakeRecursive(i2s_out->mutex, timeout)) { LOG_ERROR("xSemaphoreTakeRecursive"); return -1; } @@ -116,17 +116,17 @@ int i2s_out_open(struct i2s_out *i2s_out, const struct i2s_out_options *options) return err; } -static int i2s_out_write(struct i2s_out *i2s_out, const void *data, size_t size) +static int i2s_out_write(struct i2s_out *i2s_out, const void *data, size_t size, TickType_t timeout) { int ret = 0; - if (!xSemaphoreTakeRecursive(i2s_out->mutex, portMAX_DELAY)) { + if (!xSemaphoreTakeRecursive(i2s_out->mutex, timeout)) { LOG_ERROR("xSemaphoreTakeRecursive"); return -1; } while (size) { - if ((ret = i2s_out_dma_write(i2s_out, data, size)) < 0) { + if ((ret = i2s_out_dma_write(i2s_out, data, size, timeout)) < 0) { LOG_ERROR("i2s_out_dma_write"); break; } else if (!ret) { @@ -147,16 +147,16 @@ static int i2s_out_write(struct i2s_out *i2s_out, const void *data, size_t size) return ret; } -int i2s_out_write_serial16(struct i2s_out *i2s_out, const uint16_t data[], size_t count) +int i2s_out_write_serial16(struct i2s_out *i2s_out, const uint16_t data[], size_t count, TickType_t timeout) { - return i2s_out_write(i2s_out, data, count * sizeof(*data)); + return i2s_out_write(i2s_out, data, count * sizeof(*data), timeout); } -int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t count) +int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t count, TickType_t timeout) { int ret = 0; - if (!xSemaphoreTakeRecursive(i2s_out->mutex, portMAX_DELAY)) { + if (!xSemaphoreTakeRecursive(i2s_out->mutex, timeout)) { LOG_ERROR("xSemaphoreTakeRecursive"); return -1; } @@ -169,7 +169,7 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t void *ptr; size_t len; - if (!(len = i2s_out_dma_buffer(i2s_out, &ptr, count - index, sizeof(*buf)))) { + if (!(len = i2s_out_dma_buffer(i2s_out, &ptr, count - index, sizeof(*buf), timeout))) { LOG_WARN("i2s_out_dma_buffer: DMA buffer full"); ret = 1; goto error; @@ -196,11 +196,11 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t } #if I2S_OUT_PARALLEL_SUPPORTED - int i2s_out_write_parallel8x8(struct i2s_out *i2s_out, uint8_t *data, unsigned width) + int i2s_out_write_parallel8x8(struct i2s_out *i2s_out, uint8_t *data, unsigned width, TickType_t timeout) { int ret = 0; - if (!xSemaphoreTakeRecursive(i2s_out->mutex, portMAX_DELAY)) { + if (!xSemaphoreTakeRecursive(i2s_out->mutex, timeout)) { LOG_ERROR("xSemaphoreTakeRecursive"); return -1; } @@ -213,7 +213,7 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t void *ptr; size_t count; - if (!(count = i2s_out_dma_buffer(i2s_out, &ptr, width - index, sizeof(*buf)))) { + if (!(count = i2s_out_dma_buffer(i2s_out, &ptr, width - index, sizeof(*buf), timeout))) { LOG_WARN("i2s_out_dma_buffer: DMA buffer full"); ret = 1; goto error; @@ -239,11 +239,11 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t return ret; } - int i2s_out_write_parallel8x16(struct i2s_out *i2s_out, uint16_t *data, unsigned width) + int i2s_out_write_parallel8x16(struct i2s_out *i2s_out, uint16_t *data, unsigned width, TickType_t timeout) { int ret = 0; - if (!xSemaphoreTakeRecursive(i2s_out->mutex, portMAX_DELAY)) { + if (!xSemaphoreTakeRecursive(i2s_out->mutex, timeout)) { LOG_ERROR("xSemaphoreTakeRecursive"); return -1; } @@ -256,7 +256,7 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t void *ptr; size_t count; - if (!(count = i2s_out_dma_buffer(i2s_out, &ptr, width - index, sizeof(*buf)))) { + if (!(count = i2s_out_dma_buffer(i2s_out, &ptr, width - index, sizeof(*buf), timeout))) { LOG_WARN("i2s_out_dma_buffer: DMA buffer full"); ret = 1; goto error; @@ -282,11 +282,11 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t return ret; } - int i2s_out_write_parallel8x32(struct i2s_out *i2s_out, uint32_t *data, unsigned width) + int i2s_out_write_parallel8x32(struct i2s_out *i2s_out, uint32_t *data, unsigned width, TickType_t timeout) { int ret = 0; - if (!xSemaphoreTakeRecursive(i2s_out->mutex, portMAX_DELAY)) { + if (!xSemaphoreTakeRecursive(i2s_out->mutex, timeout)) { LOG_ERROR("xSemaphoreTakeRecursive"); return -1; } @@ -299,7 +299,7 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t void *ptr; size_t count; - if (!(count = i2s_out_dma_buffer(i2s_out, &ptr, width - index, sizeof(*buf)))) { + if (!(count = i2s_out_dma_buffer(i2s_out, &ptr, width - index, sizeof(*buf), timeout))) { LOG_WARN("i2s_out_dma_buffer: DMA buffer full"); ret = 1; goto error; @@ -348,23 +348,23 @@ int i2s_out_repeat(struct i2s_out *i2s_out, unsigned count) return err; } -int i2s_out_wait(struct i2s_out *i2s_out) +int i2s_out_wait(struct i2s_out *i2s_out, TickType_t timeout) { int err = 0; - if (!xSemaphoreTakeRecursive(i2s_out->mutex, portMAX_DELAY)) { + if (!xSemaphoreTakeRecursive(i2s_out->mutex, timeout)) { LOG_ERROR("xSemaphoreTakeRecursive"); return -1; } // wait for previous start() to complete? if (i2s_out_dma_running(i2s_out)) { - if ((err = i2s_out_dma_flush(i2s_out))) { + if ((err = i2s_out_dma_flush(i2s_out, timeout))) { LOG_ERROR("i2s_out_dma_flush"); goto error; } - if ((err = i2s_out_i2s_flush(i2s_out))) { + if ((err = i2s_out_i2s_flush(i2s_out, timeout))) { LOG_ERROR("i2s_out_i2s_flush"); goto error; } @@ -382,17 +382,17 @@ int i2s_out_wait(struct i2s_out *i2s_out) } -int i2s_out_start(struct i2s_out *i2s_out) +int i2s_out_start(struct i2s_out *i2s_out, TickType_t timeout) { int err = 0; - if (!xSemaphoreTakeRecursive(i2s_out->mutex, portMAX_DELAY)) { + if (!xSemaphoreTakeRecursive(i2s_out->mutex, timeout)) { LOG_ERROR("xSemaphoreTakeRecursive"); return -1; } // wait for previous start() to complete? - if ((err = i2s_out_wait(i2s_out))) { + if ((err = i2s_out_wait(i2s_out, timeout))) { goto error; } @@ -414,20 +414,20 @@ int i2s_out_start(struct i2s_out *i2s_out) return err; } -int i2s_out_flush(struct i2s_out *i2s_out) +int i2s_out_flush(struct i2s_out *i2s_out, TickType_t timeout) { int err = 0; - if (!xSemaphoreTakeRecursive(i2s_out->mutex, portMAX_DELAY)) { + if (!xSemaphoreTakeRecursive(i2s_out->mutex, timeout)) { LOG_ERROR("xSemaphoreTakeRecursive"); return -1; } - if ((err = i2s_out_start(i2s_out))) { + if ((err = i2s_out_start(i2s_out, timeout))) { goto error; } - if ((err = i2s_out_wait(i2s_out))) { + if ((err = i2s_out_wait(i2s_out, timeout))) { goto error; } @@ -439,9 +439,9 @@ int i2s_out_flush(struct i2s_out *i2s_out) return err; } -int i2s_out_close(struct i2s_out *i2s_out) +int i2s_out_close(struct i2s_out *i2s_out, TickType_t timeout) { - int err = i2s_out_flush(i2s_out); + int err = i2s_out_flush(i2s_out, timeout); i2s_out_dma_stop(i2s_out); i2s_out_i2s_stop(i2s_out); diff --git a/components/i2s_out/i2s_out.h b/components/i2s_out/i2s_out.h index 8361e19a..a3d18579 100644 --- a/components/i2s_out/i2s_out.h +++ b/components/i2s_out/i2s_out.h @@ -60,14 +60,14 @@ struct i2s_out { /* dma.c */ int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align, unsigned repeat); int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *options); -size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, size_t size); +size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, size_t size, TickType_t timeout); void i2s_out_dma_commit(struct i2s_out *i2s_out, unsigned count, size_t size); -int i2s_out_dma_write(struct i2s_out *i2s_out, const void *data, size_t size); +int i2s_out_dma_write(struct i2s_out *i2s_out, const void *data, size_t size, TickType_t timeout); int i2s_out_dma_repeat(struct i2s_out *i2s_out, unsigned count); int i2s_out_dma_running(struct i2s_out *i2s_out); int i2s_out_dma_pending(struct i2s_out *i2s_out); int i2s_out_dma_start(struct i2s_out *i2s_out); -int i2s_out_dma_flush(struct i2s_out *i2s_out); +int i2s_out_dma_flush(struct i2s_out *i2s_out, TickType_t timeout); void i2s_out_dma_stop(struct i2s_out *i2s_out); void i2s_out_dma_free(struct i2s_out *i2s_out); @@ -75,7 +75,7 @@ void i2s_out_dma_free(struct i2s_out *i2s_out); int i2s_out_i2s_init(struct i2s_out *i2s_out); int i2s_out_i2s_setup(struct i2s_out *i2s_out, const struct i2s_out_options *options); void i2s_out_i2s_start(struct i2s_out *i2s_out); -int i2s_out_i2s_flush(struct i2s_out *i2s_out); +int i2s_out_i2s_flush(struct i2s_out *i2s_out, TickType_t timeout); void i2s_out_i2s_stop(struct i2s_out *i2s_out); /* dev.c */ diff --git a/components/i2s_out/include/i2s_out.h b/components/i2s_out/include/i2s_out.h index 50dd5127..63b0c5ca 100644 --- a/components/i2s_out/include/i2s_out.h +++ b/components/i2s_out/include/i2s_out.h @@ -145,7 +145,7 @@ int i2s_out_new(struct i2s_out **i2s_outp, i2s_port_t port, size_t buffer_size, * * Returns <0 on error, 0 on success. */ -int i2s_out_open(struct i2s_out *i2s_out, const struct i2s_out_options *options); +int i2s_out_open(struct i2s_out *i2s_out, const struct i2s_out_options *options, TickType_t timeout); /** * Copy exactly `count` 16-bit words from `data` into the internal TX DMA buffer for serial output in little-endian order. @@ -158,7 +158,7 @@ int i2s_out_open(struct i2s_out *i2s_out, const struct i2s_out_options *options) * * Returns <0 error, 0 on success, >0 if TX buffer is full. */ -int i2s_out_write_serial16(struct i2s_out *i2s_out, const uint16_t data[], size_t count); +int i2s_out_write_serial16(struct i2s_out *i2s_out, const uint16_t data[], size_t count, TickType_t timeout); /** * Copy exactly `count` 32-bit words from `data` into the internal TX DMA buffer for serial output in little-endian order. @@ -173,7 +173,7 @@ int i2s_out_write_serial16(struct i2s_out *i2s_out, const uint16_t data[], size_ * * Returns <0 error, 0 on success, >0 if TX buffer is full. */ -int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t count); +int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t count, TickType_t timeout); #if I2S_OUT_PARALLEL_SUPPORTED /** @@ -188,7 +188,7 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t * * Returns <0 error, 0 on success, >0 if TX buffer is full. */ - int i2s_out_write_parallel8x8(struct i2s_out *i2s_out, uint8_t *data, unsigned width); + int i2s_out_write_parallel8x8(struct i2s_out *i2s_out, uint8_t *data, unsigned width, TickType_t timeout); /** * Copy 8 channels of `width` x 16-bit `data` into the internal TX DMA buffer, transposing the buffers for @@ -202,7 +202,7 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t * * Returns <0 error, 0 on success, >0 if TX buffer is full. */ - int i2s_out_write_parallel8x16(struct i2s_out *i2s_out, uint16_t *data, unsigned width); + int i2s_out_write_parallel8x16(struct i2s_out *i2s_out, uint16_t *data, unsigned width, TickType_t timeout); /** * Copy 8 channels of `width` x 32-bit `data` into the internal TX DMA buffer, transposing the buffers for @@ -216,7 +216,7 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t * * Returns <0 error, 0 on success, >0 if TX buffer is full. */ - int i2s_out_write_parallel8x32(struct i2s_out *i2s_out, uint32_t *data, unsigned width); + int i2s_out_write_parallel8x32(struct i2s_out *i2s_out, uint32_t *data, unsigned width, TickType_t timeout); #endif /** @@ -235,21 +235,21 @@ int i2s_out_repeat(struct i2s_out *i2s_out, unsigned count); * * Returns <0 on error, 0 on success. */ -int i2s_out_start(struct i2s_out *i2s_out); +int i2s_out_start(struct i2s_out *i2s_out, TickType_t timeout); /** * Start I2S output, and wait for the complete TX buffer and EOF frame to be written. * * Returns <0 on error, 0 on success. */ -int i2s_out_flush(struct i2s_out *i2s_out); +int i2s_out_flush(struct i2s_out *i2s_out, TickType_t timeout); /** * Flush I2S output, and release the lock acquired using `i2s_out_open()`. * * Tears down the data_gpio pin. */ -int i2s_out_close(struct i2s_out *i2s_out); +int i2s_out_close(struct i2s_out *i2s_out, TickType_t timeout); /** * Tear down all state setup by i2s_out_open(), including state typically shared across multiple open -> close calls. diff --git a/components/leds/include/leds.h b/components/leds/include/leds.h index c303cfe1..74b280e4 100644 --- a/components/leds/include/leds.h +++ b/components/leds/include/leds.h @@ -230,6 +230,9 @@ enum leds_interface leds_interface_for_protocol(enum leds_protocol protocol); struct i2s_out *i2s_out; + // General timeout for all i2s_out_*() operations + TickType_t timeout; + SemaphoreHandle_t pin_mutex; TickType_t pin_timeout; diff --git a/components/leds/interfaces/i2s.h b/components/leds/interfaces/i2s.h index bbf3cc15..09c47c2d 100644 --- a/components/leds/interfaces/i2s.h +++ b/components/leds/interfaces/i2s.h @@ -65,6 +65,7 @@ struct leds_interface_i2s { struct leds_interface_options_gpio gpio; struct leds_interface_i2s_stats *stats; + TickType_t timeout; }; size_t leds_interface_i2s_buffer_size(enum leds_interface_i2s_mode mode, unsigned led_count, unsigned pin_count); diff --git a/components/leds/interfaces/i2s/setup.c b/components/leds/interfaces/i2s/setup.c index f898f26c..e8f28112 100644 --- a/components/leds/interfaces/i2s/setup.c +++ b/components/leds/interfaces/i2s/setup.c @@ -138,6 +138,7 @@ int leds_interface_i2s_init(struct leds_interface_i2s *interface, const struct l interface->gpio = options->gpio; interface->stats = stats; + interface->timeout = options->timeout; return 0; } @@ -147,7 +148,7 @@ int leds_interface_i2s_setup(struct leds_interface_i2s *interface) int err = 0; WITH_STATS_TIMER(&interface->stats->open) { - if ((err = i2s_out_open(interface->i2s_out, &interface->i2s_out_options))) { + if ((err = i2s_out_open(interface->i2s_out, &interface->i2s_out_options, interface->timeout))) { LOG_ERROR("i2s_out_open"); return err; } @@ -172,7 +173,7 @@ int leds_interface_i2s_close(struct leds_interface_i2s *interface) leds_gpio_close(&interface->gpio); #endif - if ((err = i2s_out_close(interface->i2s_out))) { + if ((err = i2s_out_close(interface->i2s_out, interface->timeout))) { LOG_ERROR("i2s_out_close"); return err; } diff --git a/components/leds/interfaces/i2s/tx.c b/components/leds/interfaces/i2s/tx.c index 338506d0..117441e3 100644 --- a/components/leds/interfaces/i2s/tx.c +++ b/components/leds/interfaces/i2s/tx.c @@ -11,7 +11,7 @@ static int leds_interface_i2s_tx_32bit_bck_serial32(struct leds_interface_i2s *i // start frame uint32_t start_frame = leds_interface_i2s_mode_start_frame(interface->mode); - if ((err = i2s_out_write_serial32(interface->i2s_out, &start_frame, 1))) { + if ((err = i2s_out_write_serial32(interface->i2s_out, &start_frame, 1, interface->timeout))) { LOG_ERROR("i2s_out_write_serial32"); return err; } @@ -21,7 +21,7 @@ static int leds_interface_i2s_tx_32bit_bck_serial32(struct leds_interface_i2s *i // 32-bit pixel data interface->func.i2s_mode_32bit(interface->buf->i2s_mode_32bit, pixels, i, limit); - if ((err = i2s_out_write_serial32(interface->i2s_out, interface->buf->i2s_mode_32bit, 1))) { + if ((err = i2s_out_write_serial32(interface->i2s_out, interface->buf->i2s_mode_32bit, 1, interface->timeout))) { LOG_ERROR("i2s_out_write_serial32"); return err; } @@ -38,7 +38,7 @@ static int leds_interface_i2s_tx_24bit_4x4_serial16(struct leds_interface_i2s *i // 6x16-bit pixel data interface->func.i2s_mode_24bit_4x4(interface->buf->i2s_mode_24bit_4x4, pixels, i, limit); - if ((err = i2s_out_write_serial16(interface->i2s_out, interface->buf->i2s_mode_24bit_4x4, 6))) { + if ((err = i2s_out_write_serial16(interface->i2s_out, interface->buf->i2s_mode_24bit_4x4, 6, interface->timeout))) { LOG_ERROR("i2s_out_write_serial16"); return err; } @@ -55,7 +55,7 @@ static int leds_interface_i2s_tx_32bit_4x4_serial16(struct leds_interface_i2s *i // 8x16-bit pixel data interface->func.i2s_mode_32bit_4x4(interface->buf->i2s_mode_32bit_4x4, pixels, i, limit); - if ((err = i2s_out_write_serial16(interface->i2s_out, interface->buf->i2s_mode_32bit_4x4, 8))) { + if ((err = i2s_out_write_serial16(interface->i2s_out, interface->buf->i2s_mode_32bit_4x4, 8, interface->timeout))) { LOG_ERROR("i2s_out_write_serial16"); return err; } @@ -73,7 +73,7 @@ static int leds_interface_i2s_tx_32bit_4x4_serial16(struct leds_interface_i2s *i // start frame uint32_t start_frame[8] = { [0 ... 7] = leds_interface_i2s_mode_start_frame(interface->mode) }; - if ((err = i2s_out_write_parallel8x32(interface->i2s_out, start_frame, 1))) { + if ((err = i2s_out_write_parallel8x32(interface->i2s_out, start_frame, 1, interface->timeout))) { LOG_ERROR("i2s_out_write_parallel8x32"); return err; } @@ -84,7 +84,7 @@ static int leds_interface_i2s_tx_32bit_4x4_serial16(struct leds_interface_i2s *i interface->func.i2s_mode_32bit(interface->buf->i2s_mode_32bit_parallel8[j], pixels, j * length + i, limit); } - if ((err = i2s_out_write_parallel8x32(interface->i2s_out, (uint32_t *) interface->buf->i2s_mode_32bit_parallel8, 1))) { + if ((err = i2s_out_write_parallel8x32(interface->i2s_out, (uint32_t *) interface->buf->i2s_mode_32bit_parallel8, 1, interface->timeout))) { LOG_ERROR("i2s_out_write_parallel8x32"); return err; } @@ -104,7 +104,7 @@ static int leds_interface_i2s_tx_32bit_4x4_serial16(struct leds_interface_i2s *i interface->func.i2s_mode_24bit_4x4(interface->buf->i2s_mode_24bit_4x4_parallel8[j], pixels, j * length + i, limit); } - if ((err = i2s_out_write_parallel8x16(interface->i2s_out, (uint16_t *) interface->buf->i2s_mode_24bit_4x4_parallel8, 6))) { + if ((err = i2s_out_write_parallel8x16(interface->i2s_out, (uint16_t *) interface->buf->i2s_mode_24bit_4x4_parallel8, 6, interface->timeout))) { LOG_ERROR("i2s_out_write_parallel8x16"); return err; } @@ -124,7 +124,7 @@ static int leds_interface_i2s_tx_32bit_4x4_serial16(struct leds_interface_i2s *i interface->func.i2s_mode_32bit_4x4(interface->buf->i2s_mode_32bit_4x4_parallel8[j], pixels, j * length + i, limit); } - if ((err = i2s_out_write_parallel8x16(interface->i2s_out, (uint16_t *) interface->buf->i2s_mode_32bit_4x4_parallel8, 8))) { + if ((err = i2s_out_write_parallel8x16(interface->i2s_out, (uint16_t *) interface->buf->i2s_mode_32bit_4x4_parallel8, 8, interface->timeout))) { LOG_ERROR("i2s_out_write_parallel8x16"); return err; } @@ -205,7 +205,7 @@ int leds_interface_i2s_tx(struct leds_interface_i2s *interface, const struct led if (setup) { // sync, wait for done before close WITH_STATS_TIMER(&interface->stats->flush) { - if ((err = i2s_out_flush(interface->i2s_out))) { + if ((err = i2s_out_flush(interface->i2s_out, interface->timeout))) { LOG_ERROR("i2s_out_flush"); goto error; } @@ -213,7 +213,7 @@ int leds_interface_i2s_tx(struct leds_interface_i2s *interface, const struct led } else { // async, do not wait for done WITH_STATS_TIMER(&interface->stats->start) { - if ((err = i2s_out_start(interface->i2s_out))) { + if ((err = i2s_out_start(interface->i2s_out, interface->timeout))) { LOG_ERROR("i2s_out_start"); goto error; } diff --git a/main/leds_i2s.c b/main/leds_i2s.c index dc6c44b2..820bd608 100644 --- a/main/leds_i2s.c +++ b/main/leds_i2s.c @@ -6,6 +6,9 @@ #include +// generic timeout +#define LEDS_I2S_TIMEOUT (1000 / portTICK_RATE_MS) + // do not block if pin in use, timeout immediately #define LEDS_I2S_PIN_TIMEOUT portMAX_DELAY @@ -121,6 +124,7 @@ } options->i2s_out = leds_i2s_out[port]; + options->timeout = LEDS_I2S_TIMEOUT; #if CONFIG_IDF_TARGET_ESP8266 // TODO: use i2s_pin_mutex for arbitrary gpio pins with LEDS_I2S_GPIO_PINS_ENABLED? options->pin_mutex = pin_mutex[PIN_MUTEX_I2S0_DATA]; // shared with console uart0 From d1e3840e7ce81b3151cf42eb244bded7d32b0a06 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 21:34:53 +0200 Subject: [PATCH 22/66] i2s_out: enforce setup --- components/i2s_out/i2s_out.c | 81 +++++++++++++++++++++++++++++++++++- components/i2s_out/i2s_out.h | 1 + 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/components/i2s_out/i2s_out.c b/components/i2s_out/i2s_out.c index 74eb46f4..6dedc92d 100644 --- a/components/i2s_out/i2s_out.c +++ b/components/i2s_out/i2s_out.c @@ -108,6 +108,8 @@ int i2s_out_open(struct i2s_out *i2s_out, const struct i2s_out_options *options, goto error; } + i2s_out->setup = true; + return 0; error: @@ -125,6 +127,11 @@ static int i2s_out_write(struct i2s_out *i2s_out, const void *data, size_t size, return -1; } + if (!i2s_out->setup) { + LOG_ERROR("setup"); + return -1; + } + while (size) { if ((ret = i2s_out_dma_write(i2s_out, data, size, timeout)) < 0) { LOG_ERROR("i2s_out_dma_write"); @@ -161,6 +168,11 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t return -1; } + if (!i2s_out->setup) { + LOG_ERROR("setup"); + return -1; + } + uint32_t (*buf)[1]; unsigned index = 0; @@ -205,6 +217,11 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t return -1; } + if (!i2s_out->setup) { + LOG_ERROR("setup"); + return -1; + } + uint32_t (*buf)[2]; unsigned index = 0; @@ -248,6 +265,11 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t return -1; } + if (!i2s_out->setup) { + LOG_ERROR("setup"); + return -1; + } + uint32_t (*buf)[4]; unsigned index = 0; @@ -291,6 +313,11 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t return -1; } + if (!i2s_out->setup) { + LOG_ERROR("setup"); + return -1; + } + uint32_t (*buf)[8]; unsigned index = 0; @@ -335,6 +362,11 @@ int i2s_out_repeat(struct i2s_out *i2s_out, unsigned count) return -1; } + if (!i2s_out->setup) { + LOG_ERROR("setup"); + return -1; + } + if ((err = i2s_out_dma_repeat(i2s_out, count))) { LOG_ERROR("i2s_out_dma_repeat"); goto error; @@ -357,6 +389,11 @@ int i2s_out_wait(struct i2s_out *i2s_out, TickType_t timeout) return -1; } + if (!i2s_out->setup) { + LOG_ERROR("setup"); + return -1; + } + // wait for previous start() to complete? if (i2s_out_dma_running(i2s_out)) { if ((err = i2s_out_dma_flush(i2s_out, timeout))) { @@ -391,6 +428,11 @@ int i2s_out_start(struct i2s_out *i2s_out, TickType_t timeout) return -1; } + if (!i2s_out->setup) { + LOG_ERROR("setup"); + return -1; + } + // wait for previous start() to complete? if ((err = i2s_out_wait(i2s_out, timeout))) { goto error; @@ -423,6 +465,11 @@ int i2s_out_flush(struct i2s_out *i2s_out, TickType_t timeout) return -1; } + if (!i2s_out->setup) { + LOG_ERROR("setup"); + return -1; + } + if ((err = i2s_out_start(i2s_out, timeout))) { goto error; } @@ -441,12 +488,33 @@ int i2s_out_flush(struct i2s_out *i2s_out, TickType_t timeout) int i2s_out_close(struct i2s_out *i2s_out, TickType_t timeout) { - int err = i2s_out_flush(i2s_out, timeout); + int err = 0; + + if (!xSemaphoreTakeRecursive(i2s_out->mutex, timeout)) { + LOG_ERROR("xSemaphoreTakeRecursive"); + return -1; + } + + if (!i2s_out->setup) { + LOG_WARN("setup"); + err = 1; + goto error; + } + + err = i2s_out_flush(i2s_out, timeout); i2s_out_dma_stop(i2s_out); i2s_out_i2s_stop(i2s_out); i2s_out_pin_teardown(i2s_out); + i2s_out->setup = false; + + if (!xSemaphoreGiveRecursive(i2s_out->mutex)) { + LOG_WARN("xSemaphoreGiveRecursive"); + } + +error: + // from open() if (!xSemaphoreGiveRecursive(i2s_out->mutex)) { LOG_WARN("xSemaphoreGiveRecursive"); } @@ -456,17 +524,26 @@ int i2s_out_close(struct i2s_out *i2s_out, TickType_t timeout) int i2s_out_teardown(struct i2s_out *i2s_out) { + int err = 0; + if (!xSemaphoreTakeRecursive(i2s_out->mutex, portMAX_DELAY)) { LOG_ERROR("xSemaphoreTakeRecursive"); return -1; } + if (i2s_out->setup) { + LOG_WARN("setup"); + err = 1; + goto error; + } + i2s_out_intr_teardown(i2s_out); i2s_out_dev_teardown(i2s_out); +error: if (!xSemaphoreGiveRecursive(i2s_out->mutex)) { LOG_WARN("xSemaphoreGiveRecursive"); } - return 0; + return err; } diff --git a/components/i2s_out/i2s_out.h b/components/i2s_out/i2s_out.h index a3d18579..ba353aaf 100644 --- a/components/i2s_out/i2s_out.h +++ b/components/i2s_out/i2s_out.h @@ -23,6 +23,7 @@ struct i2s_out { portMUX_TYPE mux; #endif EventGroupHandle_t event_group; + bool setup; /* dev */ SemaphoreHandle_t dev_mutex; From bd38edb797a21e26adb9bc249ed3e81c08797db7 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 21:46:17 +0200 Subject: [PATCH 23/66] leds_interface_i2s->options --- components/leds/interfaces/i2s.h | 5 ++-- components/leds/interfaces/i2s/setup.c | 11 ++++----- components/leds/interfaces/i2s/tx.c | 32 +++++++++++++------------- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/components/leds/interfaces/i2s.h b/components/leds/interfaces/i2s.h index 09c47c2d..4047b2bc 100644 --- a/components/leds/interfaces/i2s.h +++ b/components/leds/interfaces/i2s.h @@ -51,21 +51,20 @@ union leds_interface_i2s_func { #define LEDS_INTERFACE_I2S_FUNC(type, func) ((union leds_interface_i2s_func) { .type = func }) struct leds_interface_i2s { - bool setup; // persistent setup() + const struct leds_interface_i2s_options *options; enum leds_interface_i2s_mode mode; union leds_interface_i2s_func func; union leds_interface_i2s_buf *buf; unsigned parallel; - unsigned repeat; struct i2s_out *i2s_out; struct i2s_out_options i2s_out_options; + bool i2s_out_setup; struct leds_interface_options_gpio gpio; struct leds_interface_i2s_stats *stats; - TickType_t timeout; }; size_t leds_interface_i2s_buffer_size(enum leds_interface_i2s_mode mode, unsigned led_count, unsigned pin_count); diff --git a/components/leds/interfaces/i2s/setup.c b/components/leds/interfaces/i2s/setup.c index e8f28112..bbac41f1 100644 --- a/components/leds/interfaces/i2s/setup.c +++ b/components/leds/interfaces/i2s/setup.c @@ -7,6 +7,7 @@ int leds_interface_i2s_init(struct leds_interface_i2s *interface, const struct leds_interface_i2s_options *options, enum leds_interface_i2s_mode mode, union leds_interface_i2s_func func, unsigned count, struct leds_interface_i2s_stats *stats) { + interface->options = options; interface->mode = mode; interface->func = func; @@ -15,7 +16,6 @@ int leds_interface_i2s_init(struct leds_interface_i2s *interface, const struct l #else interface->parallel = 0; #endif - interface->repeat = options->repeat; if (!(interface->buf = calloc(1, leds_interface_i2s_buf_size(interface->mode, interface->parallel)))) { LOG_ERROR("calloc"); @@ -138,7 +138,6 @@ int leds_interface_i2s_init(struct leds_interface_i2s *interface, const struct l interface->gpio = options->gpio; interface->stats = stats; - interface->timeout = options->timeout; return 0; } @@ -148,13 +147,13 @@ int leds_interface_i2s_setup(struct leds_interface_i2s *interface) int err = 0; WITH_STATS_TIMER(&interface->stats->open) { - if ((err = i2s_out_open(interface->i2s_out, &interface->i2s_out_options, interface->timeout))) { + if ((err = i2s_out_open(interface->i2s_out, &interface->i2s_out_options, interface->options->timeout))) { LOG_ERROR("i2s_out_open"); return err; } } - interface->setup = true; + interface->i2s_out_setup = true; #if CONFIG_LEDS_GPIO_ENABLED leds_gpio_setup(&interface->gpio); @@ -167,13 +166,13 @@ int leds_interface_i2s_close(struct leds_interface_i2s *interface) { int err = 0; - interface->setup = false; + interface->i2s_out_setup = false; #if CONFIG_LEDS_GPIO_ENABLED leds_gpio_close(&interface->gpio); #endif - if ((err = i2s_out_close(interface->i2s_out, interface->timeout))) { + if ((err = i2s_out_close(interface->i2s_out, interface->options->timeout))) { LOG_ERROR("i2s_out_close"); return err; } diff --git a/components/leds/interfaces/i2s/tx.c b/components/leds/interfaces/i2s/tx.c index 117441e3..e668e2f7 100644 --- a/components/leds/interfaces/i2s/tx.c +++ b/components/leds/interfaces/i2s/tx.c @@ -11,7 +11,7 @@ static int leds_interface_i2s_tx_32bit_bck_serial32(struct leds_interface_i2s *i // start frame uint32_t start_frame = leds_interface_i2s_mode_start_frame(interface->mode); - if ((err = i2s_out_write_serial32(interface->i2s_out, &start_frame, 1, interface->timeout))) { + if ((err = i2s_out_write_serial32(interface->i2s_out, &start_frame, 1, interface->options->timeout))) { LOG_ERROR("i2s_out_write_serial32"); return err; } @@ -21,7 +21,7 @@ static int leds_interface_i2s_tx_32bit_bck_serial32(struct leds_interface_i2s *i // 32-bit pixel data interface->func.i2s_mode_32bit(interface->buf->i2s_mode_32bit, pixels, i, limit); - if ((err = i2s_out_write_serial32(interface->i2s_out, interface->buf->i2s_mode_32bit, 1, interface->timeout))) { + if ((err = i2s_out_write_serial32(interface->i2s_out, interface->buf->i2s_mode_32bit, 1, interface->options->timeout))) { LOG_ERROR("i2s_out_write_serial32"); return err; } @@ -38,7 +38,7 @@ static int leds_interface_i2s_tx_24bit_4x4_serial16(struct leds_interface_i2s *i // 6x16-bit pixel data interface->func.i2s_mode_24bit_4x4(interface->buf->i2s_mode_24bit_4x4, pixels, i, limit); - if ((err = i2s_out_write_serial16(interface->i2s_out, interface->buf->i2s_mode_24bit_4x4, 6, interface->timeout))) { + if ((err = i2s_out_write_serial16(interface->i2s_out, interface->buf->i2s_mode_24bit_4x4, 6, interface->options->timeout))) { LOG_ERROR("i2s_out_write_serial16"); return err; } @@ -55,7 +55,7 @@ static int leds_interface_i2s_tx_32bit_4x4_serial16(struct leds_interface_i2s *i // 8x16-bit pixel data interface->func.i2s_mode_32bit_4x4(interface->buf->i2s_mode_32bit_4x4, pixels, i, limit); - if ((err = i2s_out_write_serial16(interface->i2s_out, interface->buf->i2s_mode_32bit_4x4, 8, interface->timeout))) { + if ((err = i2s_out_write_serial16(interface->i2s_out, interface->buf->i2s_mode_32bit_4x4, 8, interface->options->timeout))) { LOG_ERROR("i2s_out_write_serial16"); return err; } @@ -73,7 +73,7 @@ static int leds_interface_i2s_tx_32bit_4x4_serial16(struct leds_interface_i2s *i // start frame uint32_t start_frame[8] = { [0 ... 7] = leds_interface_i2s_mode_start_frame(interface->mode) }; - if ((err = i2s_out_write_parallel8x32(interface->i2s_out, start_frame, 1, interface->timeout))) { + if ((err = i2s_out_write_parallel8x32(interface->i2s_out, start_frame, 1, interface->options->timeout))) { LOG_ERROR("i2s_out_write_parallel8x32"); return err; } @@ -84,7 +84,7 @@ static int leds_interface_i2s_tx_32bit_4x4_serial16(struct leds_interface_i2s *i interface->func.i2s_mode_32bit(interface->buf->i2s_mode_32bit_parallel8[j], pixels, j * length + i, limit); } - if ((err = i2s_out_write_parallel8x32(interface->i2s_out, (uint32_t *) interface->buf->i2s_mode_32bit_parallel8, 1, interface->timeout))) { + if ((err = i2s_out_write_parallel8x32(interface->i2s_out, (uint32_t *) interface->buf->i2s_mode_32bit_parallel8, 1, interface->options->timeout))) { LOG_ERROR("i2s_out_write_parallel8x32"); return err; } @@ -104,7 +104,7 @@ static int leds_interface_i2s_tx_32bit_4x4_serial16(struct leds_interface_i2s *i interface->func.i2s_mode_24bit_4x4(interface->buf->i2s_mode_24bit_4x4_parallel8[j], pixels, j * length + i, limit); } - if ((err = i2s_out_write_parallel8x16(interface->i2s_out, (uint16_t *) interface->buf->i2s_mode_24bit_4x4_parallel8, 6, interface->timeout))) { + if ((err = i2s_out_write_parallel8x16(interface->i2s_out, (uint16_t *) interface->buf->i2s_mode_24bit_4x4_parallel8, 6, interface->options->timeout))) { LOG_ERROR("i2s_out_write_parallel8x16"); return err; } @@ -124,7 +124,7 @@ static int leds_interface_i2s_tx_32bit_4x4_serial16(struct leds_interface_i2s *i interface->func.i2s_mode_32bit_4x4(interface->buf->i2s_mode_32bit_4x4_parallel8[j], pixels, j * length + i, limit); } - if ((err = i2s_out_write_parallel8x16(interface->i2s_out, (uint16_t *) interface->buf->i2s_mode_32bit_4x4_parallel8, 8, interface->timeout))) { + if ((err = i2s_out_write_parallel8x16(interface->i2s_out, (uint16_t *) interface->buf->i2s_mode_32bit_4x4_parallel8, 8, interface->options->timeout))) { LOG_ERROR("i2s_out_write_parallel8x16"); return err; } @@ -179,10 +179,10 @@ static int leds_interface_i2s_tx_write(struct leds_interface_i2s *interface, con int leds_interface_i2s_tx(struct leds_interface_i2s *interface, const struct leds_color *pixels, unsigned count, const struct leds_limit *limit) { - bool setup = !interface->setup; // sync setup() -> write -> flush -> close()? + bool setup = interface->i2s_out_setup; // sync setup() -> write -> flush -> close()? int err = 0; - if (setup) { + if (!setup) { if ((err = leds_interface_i2s_setup(interface))) { LOG_ERROR("leds_interface_i2s_setup"); return err; @@ -195,17 +195,17 @@ int leds_interface_i2s_tx(struct leds_interface_i2s *interface, const struct led } } - if (interface->repeat) { - if ((err = i2s_out_repeat(interface->i2s_out, interface->repeat))) { + if (interface->options->repeat) { + if ((err = i2s_out_repeat(interface->i2s_out, interface->options->repeat))) { LOG_ERROR("i2s_out_repeat"); goto error; } } - if (setup) { + if (!setup) { // sync, wait for done before close WITH_STATS_TIMER(&interface->stats->flush) { - if ((err = i2s_out_flush(interface->i2s_out, interface->timeout))) { + if ((err = i2s_out_flush(interface->i2s_out, interface->options->timeout))) { LOG_ERROR("i2s_out_flush"); goto error; } @@ -213,7 +213,7 @@ int leds_interface_i2s_tx(struct leds_interface_i2s *interface, const struct led } else { // async, do not wait for done WITH_STATS_TIMER(&interface->stats->start) { - if ((err = i2s_out_start(interface->i2s_out, interface->timeout))) { + if ((err = i2s_out_start(interface->i2s_out, interface->options->timeout))) { LOG_ERROR("i2s_out_start"); goto error; } @@ -221,7 +221,7 @@ int leds_interface_i2s_tx(struct leds_interface_i2s *interface, const struct led } error: - if (setup) { + if (!setup) { if (leds_interface_i2s_close(interface)) { LOG_WARN("leds_interface_i2s_close"); } From f679c1fe111f7fa864f3c9041cf1e1d8a6a6d11b Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 21:46:32 +0200 Subject: [PATCH 24/66] main: reset leds interface on update_leds() errors --- components/leds/interfaces/i2s.h | 1 + components/leds/interfaces/i2s/setup.c | 1 - main/leds.c | 28 ++++++++++++++++++++++++++ main/leds_state.h | 5 +++++ main/leds_task.c | 3 ++- 5 files changed, 36 insertions(+), 2 deletions(-) diff --git a/components/leds/interfaces/i2s.h b/components/leds/interfaces/i2s.h index 4047b2bc..a0ee9b02 100644 --- a/components/leds/interfaces/i2s.h +++ b/components/leds/interfaces/i2s.h @@ -74,5 +74,6 @@ int leds_interface_i2s_init(struct leds_interface_i2s *interface, const struct l int leds_interface_i2s_setup(struct leds_interface_i2s *interface); int leds_interface_i2s_tx(struct leds_interface_i2s *interface, const struct leds_color *pixels, unsigned count, const struct leds_limit *limit); int leds_interface_i2s_close(struct leds_interface_i2s *interface); +int leds_interface_i2s_reset(struct leds_interface_i2s *interface); #endif diff --git a/components/leds/interfaces/i2s/setup.c b/components/leds/interfaces/i2s/setup.c index bbac41f1..270f226d 100644 --- a/components/leds/interfaces/i2s/setup.c +++ b/components/leds/interfaces/i2s/setup.c @@ -178,5 +178,4 @@ int leds_interface_i2s_close(struct leds_interface_i2s *interface) } return err; - } diff --git a/main/leds.c b/main/leds.c index d2fead68..dd4d0b9f 100644 --- a/main/leds.c +++ b/main/leds.c @@ -172,6 +172,34 @@ int setup_leds(struct leds_state *state) return 0; } +int reset_leds(struct leds_state *state) +{ + int err; + + if (!state->leds) { + LOG_ERROR("leds%d: not initialized", state->index + 1); + return -1; + } + + if (!state->config->interface_setup) { + return 0; + } + + LOG_WARN("Reset LEDS interface"); + + if ((err = leds_interface_close(state->leds))) { + LOG_WARN("leds_interface_close"); + } + + if ((err = leds_interface_setup(state->leds))) { + // crash and restart + LOG_FATAL("leds_interface_setup"); + return err; + } + + return 0; +} + int check_leds_interface(struct leds_state *state) { if (!state->leds) { diff --git a/main/leds_state.h b/main/leds_state.h index e8c91706..5a205e3e 100644 --- a/main/leds_state.h +++ b/main/leds_state.h @@ -56,6 +56,11 @@ extern struct leds_state leds_states[LEDS_COUNT]; */ int setup_leds(struct leds_state *state); +/* + * Reset leds interface setup if necessary. + */ +int reset_leds(struct leds_state *state); + /* * Update LEDs output with given USER_ACTIVITY_LEDS_* source. */ diff --git a/main/leds_task.c b/main/leds_task.c index cac7cdb9..21ead1ff 100644 --- a/main/leds_task.c +++ b/main/leds_task.c @@ -165,7 +165,8 @@ static void leds_main(void *ctx) WITH_STATS_TIMER(&stats->update) { if (update_leds(state, update_activity)) { LOG_WARN("leds%d: update_leds", state->index + 1); - continue; + user_alert(USER_ALERT_ERROR_LEDS); + reset_leds(state); } } } From 67c62d367208a76e5601a1e4c829e215bfb91d23 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 22:09:02 +0200 Subject: [PATCH 25/66] i2s_out: do not set I2S_OUT_EVENT_GROUP_BIT_DMA_EOF event bit on I2S_OUT_TOTAL_EOF_INT --- components/i2s_out/esp32/intr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index 78bef578..4598bbb6 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -26,7 +26,7 @@ void IRAM_ATTR i2s_intr_out_total_eof_handler(struct i2s_out *i2s_out, BaseType_ LOG_ISR_DEBUG("desc=%p", eof_addr); // unblock flush() tasks - xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF, task_wokenp); + xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF, task_wokenp); i2s_intr_clear(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_CLR); } From 6da9cadef6a3b5c6326214a838d51b777d81b00d Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 22:15:45 +0200 Subject: [PATCH 26/66] i2s_out: workaround unreliable I2S_OUT_TOTAL_EOF_INT --- components/i2s_out/esp32/intr.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index 4598bbb6..45199009 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -99,6 +99,14 @@ void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *tas // unblock get() task xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, task_wokenp); + // XXX: e.g. flash writes seem to drop I2S_OUT_TOTAL_EOF_INT_ST? + if (!(xEventGroupGetBitsFromISR(i2s_out->event_group) & (I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF))) { + LOG_ISR_WARN("end -> total_eof, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); + + // unblock flush() tasks + xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF, task_wokenp); + } + } else { LOG_ISR_DEBUG("ignore desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); } From 1814229f8ccb9dff92201c221026393f6bb25fe1 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Tue, 28 Oct 2025 22:30:28 +0200 Subject: [PATCH 27/66] main: bump LEDS_I2S_TIMEOUT -> 5000ms to survive config save -> flash write --- main/leds_i2s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/leds_i2s.c b/main/leds_i2s.c index 820bd608..83e52e52 100644 --- a/main/leds_i2s.c +++ b/main/leds_i2s.c @@ -7,7 +7,7 @@ #include // generic timeout -#define LEDS_I2S_TIMEOUT (1000 / portTICK_RATE_MS) +#define LEDS_I2S_TIMEOUT (5000 / portTICK_RATE_MS) // do not block if pin in use, timeout immediately #define LEDS_I2S_PIN_TIMEOUT portMAX_DELAY From 99053e544944e31d6b86b54ad3fb4f10f4757605 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 10:12:27 +0300 Subject: [PATCH 28/66] i2s_out: be more defensive about dma_write_desc->owner in wait/next() --- components/i2s_out/esp32/dma.c | 60 ++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 544e79e0..fbcf9a61 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -220,11 +220,11 @@ struct dma_desc *i2s_out_dma_wait(struct i2s_out *i2s_out, TickType_t timeout) return NULL; } } - } else { - if (i2s_out->dma_write_desc->owner) { - // last desc was already committed - return NULL; - } + } + + if (i2s_out->dma_write_desc->owner) { + LOG_WARN("dma_write_desc=%p already committed with owner=%d", i2s_out->dma_write_desc, i2s_out->dma_write_desc->owner); + return NULL; } return i2s_out->dma_write_desc; @@ -235,19 +235,32 @@ struct dma_desc *i2s_out_dma_wait(struct i2s_out *i2s_out, TickType_t timeout) */ struct dma_desc *i2s_out_dma_next(struct i2s_out *i2s_out, TickType_t timeout) { - if (!i2s_out->dma_write_desc->owner) { - // prepare for DMA - i2s_out->dma_write_desc->owner = 1; - } + if (i2s_out->dma_write_desc->owner) { + LOG_WARN("dma_write_desc=%p already committed with owner=%d", i2s_out->dma_write_desc, i2s_out->dma_write_desc->owner); + } + + LOG_DEBUG("commit desc=%p: owner=%u eof=%u buf=%p len=%u size=%u next=%p", + i2s_out->dma_write_desc, + i2s_out->dma_write_desc->owner, + i2s_out->dma_write_desc->eof, + i2s_out->dma_write_desc->buf, + i2s_out->dma_write_desc->len, + i2s_out->dma_write_desc->size, + i2s_out->dma_write_desc->next + ); - if (i2s_out->dma_write_desc < i2s_out->dma_out_desc + i2s_out->dma_out_count) { - // start using next desc - i2s_out->dma_write_desc++; - } else { - // no more descs available + // prepare for DMA + i2s_out->dma_write_desc->owner = 1; + + if (i2s_out->dma_write_desc + 1 >= i2s_out->dma_out_desc + i2s_out->dma_out_count) { + LOG_WARN("dma_write_desc=%p committed, no more descs available", i2s_out->dma_write_desc); return NULL; } + // start using next desc + i2s_out->dma_write_desc++; + + // wait for it to be available return i2s_out_dma_wait(i2s_out, timeout); } @@ -260,7 +273,7 @@ size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, s { struct dma_desc *desc; - // stop if last desc already committed + // wait for desc to be available if (!(desc = i2s_out_dma_wait(i2s_out, timeout))) { LOG_WARN("i2s_out_dma_wait"); @@ -271,25 +284,22 @@ size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, s } // advance to next desc if full - if (desc->len + size > desc->size) { - LOG_DEBUG("commit desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) < size=%u -> next=%p", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, size, desc->next); - - // commit, try with the next desc, if available - desc = i2s_out_dma_next(i2s_out, timeout); - } + if (desc->len + size <= desc->size) { + // fits - if (!desc) { + } else if (!(desc = i2s_out_dma_next(i2s_out, timeout))) { LOG_WARN("i2s_out_dma_next"); // unable to find a usable DMA buffer, timeout or TX buffers full? *ptr = NULL; return 0; - - } else if (desc->len + size > desc->size) { + } + + if (desc->len + size > desc->size) { LOG_WARN("overflow desc=%p (owner=%u eof=%u buf=%p len=%u size=%u) < size=%u", desc, desc->owner, desc->eof, desc->buf, desc->len, desc->size, size); - // unable to find a usable DMA buffer, TX buffers full + // unable to find a usable DMA buffer, size too big *ptr = NULL; return 0; From 445f1346fafdbb35193c877dfb57e4cc62783f02 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 10:27:48 +0300 Subject: [PATCH 29/66] i2s_out: fix comments --- components/i2s_out/esp32/dma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index fbcf9a61..48f37178 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -211,7 +211,7 @@ struct dma_desc *i2s_out_dma_wait(struct i2s_out *i2s_out, TickType_t timeout) { if (i2s_out->dma_start) { while (!i2s_out->dma_eof_desc || i2s_out->dma_write_desc > i2s_out->dma_eof_desc) { - LOG_DEBUG("wait for dma_write_desc=%p > dma_eof_desc=%p", i2s_out->dma_write_desc, i2s_out->dma_eof_desc); + LOG_DEBUG("wait for dma_write_desc=%p = dma_eof_desc=%p", i2s_out->dma_write_desc, i2s_out->dma_eof_desc); EventBits_t bits = xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, true, true, timeout); @@ -413,7 +413,7 @@ int i2s_out_dma_pending(struct i2s_out *i2s_out) int i2s_out_dma_running(struct i2s_out *i2s_out) { if (i2s_out->dma_start) { - // start() has been called, flush() has not + // start() has been called, stop() has not return 1; } From 68ec2b2821388e29f65f1a554ba9ae3e77b27c27 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 10:28:05 +0300 Subject: [PATCH 30/66] main: drop LEDS_I2S_INTERFACE_TIMEOUT to 1s --- main/leds_i2s.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main/leds_i2s.c b/main/leds_i2s.c index 83e52e52..26c81eef 100644 --- a/main/leds_i2s.c +++ b/main/leds_i2s.c @@ -6,8 +6,8 @@ #include -// generic timeout -#define LEDS_I2S_TIMEOUT (5000 / portTICK_RATE_MS) +// generic timeout for all i2s_out interface +#define LEDS_I2S_INTERFACE_TIMEOUT (1000 / portTICK_RATE_MS) // do not block if pin in use, timeout immediately #define LEDS_I2S_PIN_TIMEOUT portMAX_DELAY @@ -124,7 +124,7 @@ } options->i2s_out = leds_i2s_out[port]; - options->timeout = LEDS_I2S_TIMEOUT; + options->timeout = LEDS_I2S_INTERFACE_TIMEOUT; #if CONFIG_IDF_TARGET_ESP8266 // TODO: use i2s_pin_mutex for arbitrary gpio pins with LEDS_I2S_GPIO_PINS_ENABLED? options->pin_mutex = pin_mutex[PIN_MUTEX_I2S0_DATA]; // shared with console uart0 From 51014fd915606a0662facb8fca2f02d985d94aaf Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 11:03:15 +0300 Subject: [PATCH 31/66] i2s_out: use IRAM ISR to keep it enabled during cache writes --- components/i2s_out/esp32/intr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index 45199009..ec80af7a 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -9,8 +9,8 @@ #include -// use a non-shared intr -#define I2S_INTR_ALLOC_FLAGS (0) +// use a non-shared, IRAM-safe intr +#define I2S_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM) static const int i2s_irq[I2S_PORT_MAX] = { [I2S_PORT_0] = ETS_I2S0_INTR_SOURCE, From 712f61f444da240638fec305e295b274d82fbea9 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 11:04:19 +0300 Subject: [PATCH 32/66] i2s_out: TODO/XXX --- components/i2s_out/esp32/dma.c | 4 ++++ components/i2s_out/esp32/intr.c | 1 + 2 files changed, 5 insertions(+) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 48f37178..050df79d 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -402,6 +402,7 @@ int i2s_out_dma_repeat(struct i2s_out *i2s_out, unsigned count) int i2s_out_dma_pending(struct i2s_out *i2s_out) { + // XXX: this will also be true immediately after dma_start()? if (i2s_out->dma_write_desc > i2s_out->dma_out_desc || i2s_out->dma_write_desc->len > 0) { // write() happened return 1; @@ -442,6 +443,8 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) i2s_out->dma_write_desc->owner = 1; } + // TODO: this should never be NULL + // TODO: if write is shorter than total buf, then the remaining descs will be len=0 and we should just set this unconditionally? if (!i2s_out->dma_write_desc->next) { i2s_out->dma_write_desc->next = i2s_out->dma_end_desc; } @@ -498,6 +501,7 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) i2s_ll_set_out_link_addr(i2s_out->dev, (uint32_t) i2s_out->dma_out_desc); + // TODO: use *_burst_en=1 with out_eof_mode=0? i2s_ll_dma_enable_eof_on_fifo_empty(i2s_out->dev, true); i2s_ll_dma_enable_owner_check(i2s_out->dev, true); i2s_ll_dma_enable_auto_write_back(i2s_out->dev, false); diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index ec80af7a..fb130b53 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -10,6 +10,7 @@ // use a non-shared, IRAM-safe intr +// XXX: are LOG_ISR_* strings DRAM-safe? #define I2S_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM) static const int i2s_irq[I2S_PORT_MAX] = { From b9fe55389410dfa0f83e74e96684785eb48213a5 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 11:39:40 +0300 Subject: [PATCH 33/66] logging: ISR_ERROR --- components/i2s_out/esp32/intr.c | 11 +++++------ components/logging/include/logging.h | 11 ++++++++++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index fb130b53..df34cdee 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -22,33 +22,34 @@ void IRAM_ATTR i2s_intr_out_total_eof_handler(struct i2s_out *i2s_out, BaseType_ { uint32_t eof_addr; + i2s_intr_clear(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_CLR); i2s_ll_tx_get_eof_des_addr(i2s_out->dev, &eof_addr); LOG_ISR_DEBUG("desc=%p", eof_addr); // unblock flush() tasks xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF, task_wokenp); - - i2s_intr_clear(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_CLR); } void IRAM_ATTR i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out, BaseType_t *task_wokenp) { uint32_t dscr_addr; + i2s_intr_clear(i2s_out->dev, I2S_OUT_DSCR_ERR_INT_CLR); i2s_dma_tx_get_des_addr(i2s_out->dev, &dscr_addr); LOG_ISR_WARN("desc=%p", dscr_addr); - - i2s_intr_clear(i2s_out->dev, I2S_OUT_DSCR_ERR_INT_CLR); } void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *task_wokenp) { uint32_t eof_addr; + i2s_intr_clear(i2s_out->dev, I2S_OUT_EOF_INT_CLR); i2s_ll_tx_get_eof_des_addr(i2s_out->dev, &eof_addr); + LOG_ISR_DEBUG("desc=%p", eof_addr); + struct dma_desc *eof_desc = (struct dma_desc *) eof_addr; // only handle normal out_desc, not repeat_desc or end_desc @@ -111,8 +112,6 @@ void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *tas } else { LOG_ISR_DEBUG("ignore desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); } - - i2s_intr_clear(i2s_out->dev, I2S_OUT_EOF_INT_CLR); } void IRAM_ATTR i2s_intr_tx_rempty_handler(struct i2s_out *i2s_out, BaseType_t *task_wokenp) diff --git a/components/logging/include/logging.h b/components/logging/include/logging.h index f73233f2..67c5916d 100644 --- a/components/logging/include/logging.h +++ b/components/logging/include/logging.h @@ -23,12 +23,21 @@ #ifdef DEBUG #undef DEBUG #define DEBUG 1 + #define ISR_WARN 1 + #define ISR_ERROR 1 #else #define DEBUG 0 #endif #ifdef WARN #define ISR_WARN 1 + #define ISR_ERROR 1 +#else + #define ISR_WARN 0 +#endif + +#ifdef ERROR + #define ISR_ERROR 1 #else #define ISR_WARN 0 #endif @@ -39,7 +48,7 @@ #endif /* Bypass stdio buffering/interrupts, blocking write directly to UART */ -#define LOG_ISR_ERROR(fmt, ...) do { if (ISR_WARN) esp_rom_printf(LOG_FORMAT(E, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) +#define LOG_ISR_ERROR(fmt, ...) do { if (ISR_ERROR) esp_rom_printf(LOG_FORMAT(E, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) #define LOG_ISR_WARN(fmt, ...) do { if (ISR_WARN) esp_rom_printf(LOG_FORMAT(W, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) #define LOG_ISR_INFO(fmt, ...) do { if (DEBUG) esp_rom_printf(LOG_FORMAT(I, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) #define LOG_ISR_DEBUG(fmt, ...) do { if (DEBUG) esp_rom_printf(LOG_FORMAT(D, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) From 6d0ec755025c0cc5fc18e39f00cdb4bc40692dfa Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 11:40:18 +0300 Subject: [PATCH 34/66] i2s_out: LOG_ISR_ERROR on OUT_DSCR_ERR, unknown eof_desc --- components/i2s_out/esp32/intr.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index df34cdee..e31d9523 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -38,7 +38,7 @@ void IRAM_ATTR i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out, BaseType_t i2s_intr_clear(i2s_out->dev, I2S_OUT_DSCR_ERR_INT_CLR); i2s_dma_tx_get_des_addr(i2s_out->dev, &dscr_addr); - LOG_ISR_WARN("desc=%p", dscr_addr); + LOG_ISR_ERROR("desc=%p", dscr_addr); } void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *task_wokenp) @@ -74,6 +74,9 @@ void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *tas // unblock get() task xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, task_wokenp); + } else if (i2s_out->dma_repeat_desc && eof_desc >= i2s_out->dma_repeat_desc && eof_desc < i2s_out->dma_repeat_desc + i2s_out->dma_out_count * i2s_out->dma_repeat_count) { + LOG_ISR_DEBUG("ignore repeat desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); + } else if (eof_desc == i2s_out->dma_end_desc) { // speculation: we might miss EOF ISR for an intermediate DMA descriptor, and hit TOTAL_EOF with eof_addr at the end_desc @@ -103,14 +106,14 @@ void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *tas // XXX: e.g. flash writes seem to drop I2S_OUT_TOTAL_EOF_INT_ST? if (!(xEventGroupGetBitsFromISR(i2s_out->event_group) & (I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF))) { - LOG_ISR_WARN("end -> total_eof, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); + LOG_ISR_WARN("end desc=%p owner=%u len=%u -> total_eof, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); // unblock flush() tasks xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF, task_wokenp); } } else { - LOG_ISR_DEBUG("ignore desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); + LOG_ISR_ERROR("ignore desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); } } From 4300a32f9cfbc38f324f01bdc9a28ca0d4cf76c4 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 11:45:29 +0300 Subject: [PATCH 35/66] i2s_out: the I2S_OUT_TOTAL_EOF interrupt is too unreliable, stick to just I2S_OUT_EOF_INT --- components/i2s_out/esp32/dma.c | 12 ++++++------ components/i2s_out/esp32/intr.c | 28 ++-------------------------- 2 files changed, 8 insertions(+), 32 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 050df79d..b6997b23 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -183,8 +183,8 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt i2s_ll_rx_stop_link(i2s_out->dev); i2s_ll_tx_stop_link(i2s_out->dev); - i2s_intr_disable(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_ENA | I2S_OUT_DSCR_ERR_INT_ENA | I2S_OUT_EOF_INT_ENA); - i2s_intr_clear(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_CLR | I2S_OUT_DSCR_ERR_INT_CLR | I2S_OUT_EOF_INT_CLR); + i2s_intr_disable(i2s_out->dev, I2S_OUT_DSCR_ERR_INT_ENA | I2S_OUT_EOF_INT_ENA); + i2s_intr_clear(i2s_out->dev, I2S_OUT_DSCR_ERR_INT_CLR | I2S_OUT_EOF_INT_CLR); i2s_ll_enable_dma(i2s_out->dev, false); @@ -506,8 +506,8 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) i2s_ll_dma_enable_owner_check(i2s_out->dev, true); i2s_ll_dma_enable_auto_write_back(i2s_out->dev, false); - i2s_intr_clear(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_CLR | I2S_OUT_DSCR_ERR_INT_CLR | I2S_OUT_EOF_INT_CLR); - i2s_intr_enable(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_ENA | I2S_OUT_DSCR_ERR_INT_ENA | I2S_OUT_EOF_INT_ENA); + i2s_intr_clear(i2s_out->dev, I2S_OUT_DSCR_ERR_INT_CLR | I2S_OUT_EOF_INT_CLR); + i2s_intr_enable(i2s_out->dev, I2S_OUT_DSCR_ERR_INT_ENA | I2S_OUT_EOF_INT_ENA); i2s_ll_enable_dma(i2s_out->dev, true); i2s_ll_start_out_link(i2s_out->dev); @@ -546,8 +546,8 @@ void i2s_out_dma_stop(struct i2s_out *i2s_out) taskENTER_CRITICAL(&i2s_out->mux); - i2s_intr_disable(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_ENA | I2S_OUT_DSCR_ERR_INT_ENA | I2S_OUT_EOF_INT_ENA); - i2s_intr_clear(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_CLR | I2S_OUT_DSCR_ERR_INT_CLR | I2S_OUT_EOF_INT_CLR); + i2s_intr_disable(i2s_out->dev, I2S_OUT_DSCR_ERR_INT_ENA | I2S_OUT_EOF_INT_ENA); + i2s_intr_clear(i2s_out->dev, I2S_OUT_DSCR_ERR_INT_CLR | I2S_OUT_EOF_INT_CLR); i2s_ll_rx_stop_link(i2s_out->dev); i2s_ll_tx_stop_link(i2s_out->dev); diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index e31d9523..767ce3de 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -18,19 +18,6 @@ static const int i2s_irq[I2S_PORT_MAX] = { [I2S_PORT_1] = ETS_I2S1_INTR_SOURCE, }; -void IRAM_ATTR i2s_intr_out_total_eof_handler(struct i2s_out *i2s_out, BaseType_t *task_wokenp) -{ - uint32_t eof_addr; - - i2s_intr_clear(i2s_out->dev, I2S_OUT_TOTAL_EOF_INT_CLR); - i2s_ll_tx_get_eof_des_addr(i2s_out->dev, &eof_addr); - - LOG_ISR_DEBUG("desc=%p", eof_addr); - - // unblock flush() tasks - xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF, task_wokenp); -} - void IRAM_ATTR i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out, BaseType_t *task_wokenp) { uint32_t dscr_addr; @@ -101,16 +88,8 @@ void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *tas i2s_out->dma_eof_desc = eof_desc; - // unblock get() task - xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, task_wokenp); - - // XXX: e.g. flash writes seem to drop I2S_OUT_TOTAL_EOF_INT_ST? - if (!(xEventGroupGetBitsFromISR(i2s_out->event_group) & (I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF))) { - LOG_ISR_WARN("end desc=%p owner=%u len=%u -> total_eof, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); - - // unblock flush() tasks - xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF, task_wokenp); - } + // unblock get(), flush() task + xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF, task_wokenp); } else { LOG_ISR_ERROR("ignore desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); @@ -141,9 +120,6 @@ void IRAM_ATTR i2s_intr_handler(void *arg) taskENTER_CRITICAL_ISR(&i2s_out->mux); - if (int_st & I2S_OUT_TOTAL_EOF_INT_ST) { - i2s_intr_out_total_eof_handler(i2s_out, &task_woken); - } if (int_st & I2S_OUT_DSCR_ERR_INT_ST) { i2s_intr_out_dscr_err_handler(i2s_out, &task_woken); } From 59d801493267c8975287d4ccf86996cef505968e Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 11:51:40 +0300 Subject: [PATCH 36/66] i2s_out: rename I2S_OUT_EVENT_GROUP_BIT_DMA_DONE --- components/i2s_out/esp32/dma.c | 10 +++++----- components/i2s_out/esp32/intr.c | 2 +- components/i2s_out/i2s_out.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index b6997b23..97419be4 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -194,7 +194,7 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt taskEXIT_CRITICAL(&i2s_out->mux); // reset eof state - xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF); + xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_DONE); // reset write state i2s_out->dma_start = false; @@ -489,7 +489,7 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) ); // reset eof state - xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF); + xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_DONE); i2s_out->dma_eof_desc = NULL; @@ -525,9 +525,9 @@ int i2s_out_dma_flush(struct i2s_out *i2s_out, TickType_t timeout) { LOG_DEBUG("..."); - EventBits_t bits = xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF, false, false, timeout); + EventBits_t bits = xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_DONE, false, false, timeout); - if (!(bits & I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF)) { + if (!(bits & I2S_OUT_EVENT_GROUP_BIT_DMA_DONE)) { LOG_ERROR("timeout -> bits=%08x", bits); return -1; } else { @@ -560,7 +560,7 @@ void i2s_out_dma_stop(struct i2s_out *i2s_out) taskEXIT_CRITICAL(&i2s_out->mux); // reset eof state - xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF); + xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_DONE); i2s_out->dma_eof_desc = NULL; } diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index 767ce3de..5ec90607 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -89,7 +89,7 @@ void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *tas i2s_out->dma_eof_desc = eof_desc; // unblock get(), flush() task - xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF, task_wokenp); + xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_DONE, task_wokenp); } else { LOG_ISR_ERROR("ignore desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); diff --git a/components/i2s_out/i2s_out.h b/components/i2s_out/i2s_out.h index ba353aaf..7f9d153a 100644 --- a/components/i2s_out/i2s_out.h +++ b/components/i2s_out/i2s_out.h @@ -13,7 +13,7 @@ struct dma_desc; #define I2S_OUT_EVENT_GROUP_BIT_DMA_EOF (1 << 0) -#define I2S_OUT_EVENT_GROUP_BIT_DMA_TOTAL_EOF (1 << 1) +#define I2S_OUT_EVENT_GROUP_BIT_DMA_DONE (1 << 1) #define I2S_OUT_EVENT_GROUP_BIT_I2S_EOF (1 << 2) struct i2s_out { From 42ca510b812c66694ac15d1b1bf79c94516b6038 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 11:58:46 +0300 Subject: [PATCH 37/66] i2s_out: simplify i2s_out_dma_wait() condition, fix deadlock on dma_eof_desc -> dma_end_desc --- components/i2s_out/esp32/dma.c | 4 ++-- components/i2s_out/esp32/intr.c | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 97419be4..ab262e9e 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -210,8 +210,8 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt struct dma_desc *i2s_out_dma_wait(struct i2s_out *i2s_out, TickType_t timeout) { if (i2s_out->dma_start) { - while (!i2s_out->dma_eof_desc || i2s_out->dma_write_desc > i2s_out->dma_eof_desc) { - LOG_DEBUG("wait for dma_write_desc=%p = dma_eof_desc=%p", i2s_out->dma_write_desc, i2s_out->dma_eof_desc); + while (i2s_out->dma_write_desc->owner) { + LOG_DEBUG("wait for dma_eof_desc=%p vs dma_write_desc=%p, dma_end_desc=%p", i2s_out->dma_eof_desc, i2s_out->dma_write_desc, i2s_out->dma_end_desc); EventBits_t bits = xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, true, true, timeout); diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index 5ec90607..501bef05 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -43,7 +43,7 @@ void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *tas if (eof_desc >= i2s_out->dma_out_desc && eof_desc < i2s_out->dma_out_desc + i2s_out->dma_out_count) { LOG_ISR_DEBUG("eof desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); - // speculation: we may miss sone EOF ISRs + // we may miss some EOF ISR for intermediate DMA descriptors, unblock i2s_out_dma_wait() if (i2s_out->dma_eof_desc) { for (struct dma_desc *desc = eof_desc; desc > i2s_out->dma_eof_desc; desc--) { if (desc != eof_desc) { @@ -66,8 +66,7 @@ void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *tas } else if (eof_desc == i2s_out->dma_end_desc) { - // speculation: we might miss EOF ISR for an intermediate DMA descriptor, and hit TOTAL_EOF with eof_addr at the end_desc - // resulting in wait() deadlocking on dma_eof_desc not being updated + // we may miss some EOF ISR for intermediate DMA descriptors, unblock i2s_out_dma_wait() eof_desc = i2s_out->dma_out_desc + i2s_out->dma_out_count - 1; if (!i2s_out->dma_eof_desc) { From ef37dba0339eb4d0c6d72f2175eaff3f61285bea Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 12:31:52 +0300 Subject: [PATCH 38/66] i2s_out: include dma state in flush timeout error --- components/i2s_out/esp32/dma.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index ab262e9e..a3be6391 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -528,7 +528,13 @@ int i2s_out_dma_flush(struct i2s_out *i2s_out, TickType_t timeout) EventBits_t bits = xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_DONE, false, false, timeout); if (!(bits & I2S_OUT_EVENT_GROUP_BIT_DMA_DONE)) { - LOG_ERROR("timeout -> bits=%08x", bits); + LOG_ERROR("timeout -> bits=%08x, dma_out_desc=%p...%p dma_write_desc=%p dma_end_desc=%p dma_eof_desc=%p", + bits, + i2s_out->dma_out_desc, i2s_out->dma_out_desc + i2s_out->dma_out_count - 1, + i2s_out->dma_write_desc, + i2s_out->dma_end_desc, + i2s_out->dma_eof_desc + ); return -1; } else { LOG_DEBUG("wait -> bits=%08x", bits); From 4e20ff576f09a9b491b247d8e0042c4ffeea15ed Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 12:34:49 +0300 Subject: [PATCH 39/66] i2s_out: avoid setting dma_write_desc->next at dma_start(), it is only initialized in setup() --- components/i2s_out/esp32/dma.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index a3be6391..236339e5 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -443,12 +443,6 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) i2s_out->dma_write_desc->owner = 1; } - // TODO: this should never be NULL - // TODO: if write is shorter than total buf, then the remaining descs will be len=0 and we should just set this unconditionally? - if (!i2s_out->dma_write_desc->next) { - i2s_out->dma_write_desc->next = i2s_out->dma_end_desc; - } - i2s_out->dma_end_desc->owner = 1; i2s_out->dma_end_desc->next = NULL; From 7c642cb91662e8affd52bbee3b97b0f14c18c894 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 12:35:05 +0300 Subject: [PATCH 40/66] i2s_out: ensure dma_end_desc->eof=1 in dma_start() --- components/i2s_out/esp32/dma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 236339e5..fcf27408 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -443,6 +443,7 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) i2s_out->dma_write_desc->owner = 1; } + i2s_out->dma_end_desc->eof = 1; i2s_out->dma_end_desc->owner = 1; i2s_out->dma_end_desc->next = NULL; From 18e197b78584e3cd58db923c8ee7e03f38bf30c5 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 12:35:15 +0300 Subject: [PATCH 41/66] main: enable leds interface_setup by default --- main/leds_configtab.i | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/leds_configtab.i b/main/leds_configtab.i index e6568017..66cae892 100644 --- a/main/leds_configtab.i +++ b/main/leds_configtab.i @@ -15,7 +15,7 @@ const struct configtab LEDS_CONFIGTAB[] = { }, { CONFIG_TYPE_BOOL, "interface_setup", .description = "Setup dedicated interface, operate in async mode. I2S only.", - .bool_type = { .value = &LEDS_CONFIG.interface_setup, .default_value = false }, + .bool_type = { .value = &LEDS_CONFIG.interface_setup, .default_value = true }, }, { CONFIG_TYPE_ENUM, "protocol", .enum_type = { .value = &LEDS_CONFIG.protocol, .values = leds_protocol_enum }, From 52ee9bf360d152803cd7546004857a66551bff9d Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 12:44:17 +0300 Subject: [PATCH 42/66] logging: use DRAM_STR for ISR logging (IRAM-safe) --- components/i2s_out/esp32/intr.c | 1 - components/logging/include/logging.h | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index 501bef05..9f403c25 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -10,7 +10,6 @@ // use a non-shared, IRAM-safe intr -// XXX: are LOG_ISR_* strings DRAM-safe? #define I2S_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM) static const int i2s_irq[I2S_PORT_MAX] = { diff --git a/components/logging/include/logging.h b/components/logging/include/logging.h index 67c5916d..59aa1813 100644 --- a/components/logging/include/logging.h +++ b/components/logging/include/logging.h @@ -48,10 +48,10 @@ #endif /* Bypass stdio buffering/interrupts, blocking write directly to UART */ -#define LOG_ISR_ERROR(fmt, ...) do { if (ISR_ERROR) esp_rom_printf(LOG_FORMAT(E, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) -#define LOG_ISR_WARN(fmt, ...) do { if (ISR_WARN) esp_rom_printf(LOG_FORMAT(W, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) -#define LOG_ISR_INFO(fmt, ...) do { if (DEBUG) esp_rom_printf(LOG_FORMAT(I, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) -#define LOG_ISR_DEBUG(fmt, ...) do { if (DEBUG) esp_rom_printf(LOG_FORMAT(D, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) +#define LOG_ISR_ERROR(fmt, ...) do { if (ISR_ERROR) esp_rom_printf(DRAM_STR(LOG_FORMAT(E, fmt)), esp_log_timestamp(), (__func__), ##__VA_ARGS__); } while(0) +#define LOG_ISR_WARN(fmt, ...) do { if (ISR_WARN) esp_rom_printf(DRAM_STR(LOG_FORMAT(W, fmt)), esp_log_timestamp(), (__func__), ##__VA_ARGS__); } while(0) +#define LOG_ISR_INFO(fmt, ...) do { if (DEBUG) esp_rom_printf(DRAM_STR(LOG_FORMAT(I, fmt)), esp_log_timestamp(), (__func__), ##__VA_ARGS__); } while(0) +#define LOG_ISR_DEBUG(fmt, ...) do { if (DEBUG) esp_rom_printf(DRAM_STR(LOG_FORMAT(D, fmt)), esp_log_timestamp(), (__func__), ##__VA_ARGS__); } while(0) /* Bypass stdio buffering/interrupts, blocking write directly to UART */ #define LOG_BOOT_ERROR(fmt, ...) do { esp_rom_printf(LOG_FORMAT(E, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) From 9fdecab213252dd519e27524385033776e27145c Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 12:44:31 +0300 Subject: [PATCH 43/66] logging: fix DEBUG -> WARN -> ERROR --- components/logging/include/logging.h | 31 ++++++++++++++++------------ 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/components/logging/include/logging.h b/components/logging/include/logging.h index 59aa1813..f083ce28 100644 --- a/components/logging/include/logging.h +++ b/components/logging/include/logging.h @@ -13,33 +13,38 @@ #include #include -#ifdef TRACE - #undef TRACE - #define TRACE 1 +#ifdef ERROR + #define ISR_ERROR 1 #else - #define TRACE 0 + #define ISR_ERROR 0 #endif -#ifdef DEBUG - #undef DEBUG - #define DEBUG 1 +#ifdef WARN + #undef ISR_ERROR + #define ISR_WARN 1 #define ISR_ERROR 1 #else - #define DEBUG 0 + #define ISR_WARN 0 #endif -#ifdef WARN +#ifdef DEBUG + #undef DEBUG + #undef ISR_WARN + #undef ISR_ERROR + + #define DEBUG 1 #define ISR_WARN 1 #define ISR_ERROR 1 #else - #define ISR_WARN 0 + #define DEBUG 0 #endif -#ifdef ERROR - #define ISR_ERROR 1 +#ifdef TRACE + #undef TRACE + #define TRACE 1 #else - #define ISR_WARN 0 + #define TRACE 0 #endif #if CONFIG_LOG_COLORS From 015013064ca2e9820eceda4a7c93f880f86620c2 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 12:44:41 +0300 Subject: [PATCH 44/66] usb_pd_sink: cleanup DEBUG --- components/usb_pd_sink/stusb4500_nvm.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/usb_pd_sink/stusb4500_nvm.c b/components/usb_pd_sink/stusb4500_nvm.c index cfe6a3ff..9ec1f34f 100644 --- a/components/usb_pd_sink/stusb4500_nvm.c +++ b/components/usb_pd_sink/stusb4500_nvm.c @@ -4,8 +4,6 @@ #include -#define DEBUG - #include static int stusb4500_nvm_unlock(struct stusb4500 *stusb4500) From bed642ce76fd56523553911d6df18c4d7c34d9bb Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 16:00:19 +0300 Subject: [PATCH 45/66] i2s_out: simplify dma_end_desc case --- components/i2s_out/esp32/intr.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index 9f403c25..a8c153db 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -66,22 +66,12 @@ void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *tas } else if (eof_desc == i2s_out->dma_end_desc) { // we may miss some EOF ISR for intermediate DMA descriptors, unblock i2s_out_dma_wait() - eof_desc = i2s_out->dma_out_desc + i2s_out->dma_out_count - 1; - - if (!i2s_out->dma_eof_desc) { - LOG_ISR_WARN("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); - - for (struct dma_desc *desc = eof_desc; desc >= i2s_out->dma_out_desc; desc--) { - i2s_dma_desc_reset(desc); - } - } else if (i2s_out->dma_eof_desc != eof_desc) { - LOG_ISR_WARN("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); + LOG_ISR_WARN("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); - for (struct dma_desc *desc = eof_desc; desc > i2s_out->dma_eof_desc; desc--) { - i2s_dma_desc_reset(desc); - } - } else { - LOG_ISR_DEBUG("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); + eof_desc = i2s_out->dma_out_desc + i2s_out->dma_out_count - 1; + + for (struct dma_desc *desc = eof_desc; desc > i2s_out->dma_eof_desc; desc--) { + i2s_dma_desc_reset(desc); } i2s_out->dma_eof_desc = eof_desc; From 625878d380639e44eb75d1bbfc37e4210fa4b29d Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 16:02:22 +0300 Subject: [PATCH 46/66] i2s_out: refactor xEventGroupSetBitsFromISR() usage to a single call --- components/i2s_out/esp32/intr.c | 35 ++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index a8c153db..839b4e76 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -17,7 +17,7 @@ static const int i2s_irq[I2S_PORT_MAX] = { [I2S_PORT_1] = ETS_I2S1_INTR_SOURCE, }; -void IRAM_ATTR i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out, BaseType_t *task_wokenp) +EventBits_t IRAM_ATTR i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out) { uint32_t dscr_addr; @@ -25,9 +25,11 @@ void IRAM_ATTR i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out, BaseType_t i2s_dma_tx_get_des_addr(i2s_out->dev, &dscr_addr); LOG_ISR_ERROR("desc=%p", dscr_addr); + + return 0; } -void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *task_wokenp) +EventBits_t IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out) { uint32_t eof_addr; @@ -58,13 +60,14 @@ void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *tas i2s_out->dma_eof_desc = eof_desc; // unblock get() task - xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, task_wokenp); + return I2S_OUT_EVENT_GROUP_BIT_DMA_EOF; } else if (i2s_out->dma_repeat_desc && eof_desc >= i2s_out->dma_repeat_desc && eof_desc < i2s_out->dma_repeat_desc + i2s_out->dma_out_count * i2s_out->dma_repeat_count) { LOG_ISR_DEBUG("ignore repeat desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); - } else if (eof_desc == i2s_out->dma_end_desc) { + return 0; + } else if (eof_desc == i2s_out->dma_end_desc) { // we may miss some EOF ISR for intermediate DMA descriptors, unblock i2s_out_dma_wait() LOG_ISR_WARN("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); @@ -75,31 +78,34 @@ void IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *tas } i2s_out->dma_eof_desc = eof_desc; + i2s_out->dma_done = true; // unblock get(), flush() task - xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_DONE, task_wokenp); + return I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_DONE; } else { LOG_ISR_ERROR("ignore desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); + + return 0; } } -void IRAM_ATTR i2s_intr_tx_rempty_handler(struct i2s_out *i2s_out, BaseType_t *task_wokenp) +EventBits_t IRAM_ATTR i2s_intr_tx_rempty_handler(struct i2s_out *i2s_out) { LOG_ISR_DEBUG(""); - // unblock flush() task - xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF, task_wokenp); - // interrupt will fire until disabled i2s_intr_disable(i2s_out->dev, I2S_TX_REMPTY_INT_ENA); i2s_intr_clear(i2s_out->dev, I2S_TX_REMPTY_INT_CLR); + + return I2S_OUT_EVENT_GROUP_BIT_I2S_EOF; } void IRAM_ATTR i2s_intr_handler(void *arg) { struct i2s_out *i2s_out = arg; uint32_t int_st = i2s_ll_get_intr_status(i2s_out->dev); + EventBits_t event_bits = 0; BaseType_t task_woken = pdFALSE; if (!int_st) { @@ -109,13 +115,18 @@ void IRAM_ATTR i2s_intr_handler(void *arg) taskENTER_CRITICAL_ISR(&i2s_out->mux); if (int_st & I2S_OUT_DSCR_ERR_INT_ST) { - i2s_intr_out_dscr_err_handler(i2s_out, &task_woken); + event_bits |= i2s_intr_out_dscr_err_handler(i2s_out); } if (int_st & I2S_OUT_EOF_INT_ST) { - i2s_intr_out_eof_handler(i2s_out, &task_woken); + event_bits |= i2s_intr_out_eof_handler(i2s_out); } if (int_st & I2S_TX_REMPTY_INT_ST) { - i2s_intr_tx_rempty_handler(i2s_out, &task_woken); + event_bits |= i2s_intr_tx_rempty_handler(i2s_out); + } + + if (event_bits) { + // XXX: loops via the timer daemon task, replace with a semaphore? + xEventGroupSetBitsFromISR(i2s_out->event_group, event_bits, &task_woken); } taskEXIT_CRITICAL_ISR(&i2s_out->mux); From 666c0685b5e80e75a0ba8e2d17c900e08e04cf62 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 16:04:23 +0300 Subject: [PATCH 47/66] i2s_out: log xEventGroupSetBitsFromISR() failures --- components/i2s_out/esp32/intr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index 839b4e76..19c79fc0 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -126,7 +126,9 @@ void IRAM_ATTR i2s_intr_handler(void *arg) if (event_bits) { // XXX: loops via the timer daemon task, replace with a semaphore? - xEventGroupSetBitsFromISR(i2s_out->event_group, event_bits, &task_woken); + if (xEventGroupSetBitsFromISR(i2s_out->event_group, event_bits, &task_woken) == pdFALSE) { + LOG_ISR_ERROR("xEventGroupSetBitsFromISR"); + } } taskEXIT_CRITICAL_ISR(&i2s_out->mux); From 6e9c3f9ab79dbc9635302b280f8abb296ba2c927 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 16:05:39 +0300 Subject: [PATCH 48/66] i2s_out: split intr_iram for linker iram/dram, including logging --- components/i2s_out/CMakeLists.txt | 1 + components/i2s_out/esp32.lf | 4 + components/i2s_out/esp32/intr.c | 121 ------------------------- components/i2s_out/esp32/intr.h | 2 + components/i2s_out/esp32/intr_iram.c | 126 +++++++++++++++++++++++++++ 5 files changed, 133 insertions(+), 121 deletions(-) create mode 100644 components/i2s_out/esp32.lf create mode 100644 components/i2s_out/esp32/intr_iram.c diff --git a/components/i2s_out/CMakeLists.txt b/components/i2s_out/CMakeLists.txt index f98b8e25..9ceaebd6 100644 --- a/components/i2s_out/CMakeLists.txt +++ b/components/i2s_out/CMakeLists.txt @@ -2,6 +2,7 @@ idf_component_register( SRC_DIRS . ${IDF_TARGET} INCLUDE_DIRS "include" PRIV_REQUIRES logging + LDFRAGMENTS ${IDF_TARGET}.lf ) target_compile_options(${COMPONENT_LIB} PRIVATE -O2) diff --git a/components/i2s_out/esp32.lf b/components/i2s_out/esp32.lf new file mode 100644 index 00000000..4474a170 --- /dev/null +++ b/components/i2s_out/esp32.lf @@ -0,0 +1,4 @@ +[mapping:i2s_out] +archive: i2s_out.a +entries: + intr_iram (noflash) diff --git a/components/i2s_out/esp32/intr.c b/components/i2s_out/esp32/intr.c index 19c79fc0..727f1244 100644 --- a/components/i2s_out/esp32/intr.c +++ b/components/i2s_out/esp32/intr.c @@ -17,127 +17,6 @@ static const int i2s_irq[I2S_PORT_MAX] = { [I2S_PORT_1] = ETS_I2S1_INTR_SOURCE, }; -EventBits_t IRAM_ATTR i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out) -{ - uint32_t dscr_addr; - - i2s_intr_clear(i2s_out->dev, I2S_OUT_DSCR_ERR_INT_CLR); - i2s_dma_tx_get_des_addr(i2s_out->dev, &dscr_addr); - - LOG_ISR_ERROR("desc=%p", dscr_addr); - - return 0; -} - -EventBits_t IRAM_ATTR i2s_intr_out_eof_handler(struct i2s_out *i2s_out) -{ - uint32_t eof_addr; - - i2s_intr_clear(i2s_out->dev, I2S_OUT_EOF_INT_CLR); - i2s_ll_tx_get_eof_des_addr(i2s_out->dev, &eof_addr); - - LOG_ISR_DEBUG("desc=%p", eof_addr); - - struct dma_desc *eof_desc = (struct dma_desc *) eof_addr; - - // only handle normal out_desc, not repeat_desc or end_desc - if (eof_desc >= i2s_out->dma_out_desc && eof_desc < i2s_out->dma_out_desc + i2s_out->dma_out_count) { - LOG_ISR_DEBUG("eof desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); - - // we may miss some EOF ISR for intermediate DMA descriptors, unblock i2s_out_dma_wait() - if (i2s_out->dma_eof_desc) { - for (struct dma_desc *desc = eof_desc; desc > i2s_out->dma_eof_desc; desc--) { - if (desc != eof_desc) { - LOG_ISR_WARN("miss desc=%p owner=%u len=%u", desc, desc->owner, desc->len); - } - - i2s_dma_desc_reset(desc); - } - } else { - i2s_dma_desc_reset(eof_desc); - } - - i2s_out->dma_eof_desc = eof_desc; - - // unblock get() task - return I2S_OUT_EVENT_GROUP_BIT_DMA_EOF; - - } else if (i2s_out->dma_repeat_desc && eof_desc >= i2s_out->dma_repeat_desc && eof_desc < i2s_out->dma_repeat_desc + i2s_out->dma_out_count * i2s_out->dma_repeat_count) { - LOG_ISR_DEBUG("ignore repeat desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); - - return 0; - - } else if (eof_desc == i2s_out->dma_end_desc) { - // we may miss some EOF ISR for intermediate DMA descriptors, unblock i2s_out_dma_wait() - LOG_ISR_WARN("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); - - eof_desc = i2s_out->dma_out_desc + i2s_out->dma_out_count - 1; - - for (struct dma_desc *desc = eof_desc; desc > i2s_out->dma_eof_desc; desc--) { - i2s_dma_desc_reset(desc); - } - - i2s_out->dma_eof_desc = eof_desc; - i2s_out->dma_done = true; - - // unblock get(), flush() task - return I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_DONE; - - } else { - LOG_ISR_ERROR("ignore desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); - - return 0; - } -} - -EventBits_t IRAM_ATTR i2s_intr_tx_rempty_handler(struct i2s_out *i2s_out) -{ - LOG_ISR_DEBUG(""); - - // interrupt will fire until disabled - i2s_intr_disable(i2s_out->dev, I2S_TX_REMPTY_INT_ENA); - i2s_intr_clear(i2s_out->dev, I2S_TX_REMPTY_INT_CLR); - - return I2S_OUT_EVENT_GROUP_BIT_I2S_EOF; -} - -void IRAM_ATTR i2s_intr_handler(void *arg) -{ - struct i2s_out *i2s_out = arg; - uint32_t int_st = i2s_ll_get_intr_status(i2s_out->dev); - EventBits_t event_bits = 0; - BaseType_t task_woken = pdFALSE; - - if (!int_st) { - return; - } - - taskENTER_CRITICAL_ISR(&i2s_out->mux); - - if (int_st & I2S_OUT_DSCR_ERR_INT_ST) { - event_bits |= i2s_intr_out_dscr_err_handler(i2s_out); - } - if (int_st & I2S_OUT_EOF_INT_ST) { - event_bits |= i2s_intr_out_eof_handler(i2s_out); - } - if (int_st & I2S_TX_REMPTY_INT_ST) { - event_bits |= i2s_intr_tx_rempty_handler(i2s_out); - } - - if (event_bits) { - // XXX: loops via the timer daemon task, replace with a semaphore? - if (xEventGroupSetBitsFromISR(i2s_out->event_group, event_bits, &task_woken) == pdFALSE) { - LOG_ISR_ERROR("xEventGroupSetBitsFromISR"); - } - } - - taskEXIT_CRITICAL_ISR(&i2s_out->mux); - - if (task_woken == pdTRUE) { - portYIELD_FROM_ISR(); - } -} - int i2s_out_intr_setup(struct i2s_out *i2s_out, const struct i2s_out_options *options) { esp_err_t err = 0; diff --git a/components/i2s_out/esp32/intr.h b/components/i2s_out/esp32/intr.h index 6dab1e67..c0175994 100644 --- a/components/i2s_out/esp32/intr.h +++ b/components/i2s_out/esp32/intr.h @@ -22,3 +22,5 @@ static inline void i2s_intr_disable_all(i2s_dev_t *hw) { hw->int_ena.val = 0; } + +void i2s_intr_handler(void *arg); diff --git a/components/i2s_out/esp32/intr_iram.c b/components/i2s_out/esp32/intr_iram.c new file mode 100644 index 00000000..6a4dcb51 --- /dev/null +++ b/components/i2s_out/esp32/intr_iram.c @@ -0,0 +1,126 @@ +#include "../i2s_out.h" +#include "dma.h" +#include "intr.h" + +#include + +EventBits_t i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out) +{ + uint32_t dscr_addr; + + i2s_intr_clear(i2s_out->dev, I2S_OUT_DSCR_ERR_INT_CLR); + i2s_dma_tx_get_des_addr(i2s_out->dev, &dscr_addr); + + LOG_ISR_ERROR("desc=%p", dscr_addr); + + return 0; +} + +EventBits_t i2s_intr_out_eof_handler(struct i2s_out *i2s_out) +{ + uint32_t eof_addr; + + i2s_intr_clear(i2s_out->dev, I2S_OUT_EOF_INT_CLR); + i2s_ll_tx_get_eof_des_addr(i2s_out->dev, &eof_addr); + + LOG_ISR_DEBUG("desc=%p", eof_addr); + + struct dma_desc *eof_desc = (struct dma_desc *) eof_addr; + + // only handle normal out_desc, not repeat_desc or end_desc + if (eof_desc >= i2s_out->dma_out_desc && eof_desc < i2s_out->dma_out_desc + i2s_out->dma_out_count) { + LOG_ISR_DEBUG("eof desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); + + // we may miss some EOF ISR for intermediate DMA descriptors, unblock i2s_out_dma_wait() + if (i2s_out->dma_eof_desc) { + for (struct dma_desc *desc = eof_desc; desc > i2s_out->dma_eof_desc; desc--) { + if (desc != eof_desc) { + LOG_ISR_WARN("miss desc=%p owner=%u len=%u", desc, desc->owner, desc->len); + } + + i2s_dma_desc_reset(desc); + } + } else { + i2s_dma_desc_reset(eof_desc); + } + + i2s_out->dma_eof_desc = eof_desc; + + // unblock get() task + return I2S_OUT_EVENT_GROUP_BIT_DMA_EOF; + + } else if (i2s_out->dma_repeat_desc && eof_desc >= i2s_out->dma_repeat_desc && eof_desc < i2s_out->dma_repeat_desc + i2s_out->dma_out_count * i2s_out->dma_repeat_count) { + LOG_ISR_DEBUG("ignore repeat desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); + + return 0; + + } else if (eof_desc == i2s_out->dma_end_desc) { + // we may miss some EOF ISR for intermediate DMA descriptors, unblock i2s_out_dma_wait() + LOG_ISR_WARN("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); + + eof_desc = i2s_out->dma_out_desc + i2s_out->dma_out_count - 1; + + for (struct dma_desc *desc = eof_desc; desc > i2s_out->dma_eof_desc; desc--) { + i2s_dma_desc_reset(desc); + } + + i2s_out->dma_eof_desc = eof_desc; + i2s_out->dma_done = true; + + // unblock get(), flush() task + return I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_DONE; + + } else { + LOG_ISR_ERROR("ignore desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); + + return 0; + } +} + +EventBits_t i2s_intr_tx_rempty_handler(struct i2s_out *i2s_out) +{ + LOG_ISR_DEBUG(""); + + // interrupt will fire until disabled + i2s_intr_disable(i2s_out->dev, I2S_TX_REMPTY_INT_ENA); + i2s_intr_clear(i2s_out->dev, I2S_TX_REMPTY_INT_CLR); + + return I2S_OUT_EVENT_GROUP_BIT_I2S_EOF; +} + +void i2s_intr_handler(void *arg) +{ + struct i2s_out *i2s_out = arg; + uint32_t int_st = i2s_ll_get_intr_status(i2s_out->dev); + EventBits_t event_bits = 0; + BaseType_t task_woken = pdFALSE; + + if (!int_st) { + return; + } + + taskENTER_CRITICAL_ISR(&i2s_out->mux); + + if (int_st & I2S_OUT_DSCR_ERR_INT_ST) { + event_bits |= i2s_intr_out_dscr_err_handler(i2s_out); + } + if (int_st & I2S_OUT_EOF_INT_ST) { + event_bits |= i2s_intr_out_eof_handler(i2s_out); + } + if (int_st & I2S_TX_REMPTY_INT_ST) { + event_bits |= i2s_intr_tx_rempty_handler(i2s_out); + } + + if (event_bits) { + // XXX: loops via the timer daemon task, replace with a semaphore? + if (xEventGroupSetBitsFromISR(i2s_out->event_group, event_bits, &task_woken) == pdFALSE) { + LOG_ISR_ERROR("xEventGroupSetBitsFromISR"); + } + } + + taskEXIT_CRITICAL_ISR(&i2s_out->mux); + + if (task_woken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} From 95b7036e70adfc1884c27306a842a4a5cebc0339 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 16:06:38 +0300 Subject: [PATCH 49/66] logging: drop DRAM_STR for LOG_FORMAT, require use of linker script for __func__ with iram isr --- components/logging/include/logging.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/logging/include/logging.h b/components/logging/include/logging.h index f083ce28..0a340fd2 100644 --- a/components/logging/include/logging.h +++ b/components/logging/include/logging.h @@ -53,10 +53,10 @@ #endif /* Bypass stdio buffering/interrupts, blocking write directly to UART */ -#define LOG_ISR_ERROR(fmt, ...) do { if (ISR_ERROR) esp_rom_printf(DRAM_STR(LOG_FORMAT(E, fmt)), esp_log_timestamp(), (__func__), ##__VA_ARGS__); } while(0) -#define LOG_ISR_WARN(fmt, ...) do { if (ISR_WARN) esp_rom_printf(DRAM_STR(LOG_FORMAT(W, fmt)), esp_log_timestamp(), (__func__), ##__VA_ARGS__); } while(0) -#define LOG_ISR_INFO(fmt, ...) do { if (DEBUG) esp_rom_printf(DRAM_STR(LOG_FORMAT(I, fmt)), esp_log_timestamp(), (__func__), ##__VA_ARGS__); } while(0) -#define LOG_ISR_DEBUG(fmt, ...) do { if (DEBUG) esp_rom_printf(DRAM_STR(LOG_FORMAT(D, fmt)), esp_log_timestamp(), (__func__), ##__VA_ARGS__); } while(0) +#define LOG_ISR_ERROR(fmt, ...) do { if (ISR_ERROR) esp_rom_printf(LOG_FORMAT(E, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) +#define LOG_ISR_WARN(fmt, ...) do { if (ISR_WARN) esp_rom_printf(LOG_FORMAT(W, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) +#define LOG_ISR_INFO(fmt, ...) do { if (DEBUG) esp_rom_printf(LOG_FORMAT(I, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) +#define LOG_ISR_DEBUG(fmt, ...) do { if (DEBUG) esp_rom_printf(LOG_FORMAT(D, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) /* Bypass stdio buffering/interrupts, blocking write directly to UART */ #define LOG_BOOT_ERROR(fmt, ...) do { esp_rom_printf(LOG_FORMAT(E, fmt), esp_log_timestamp(), __func__, ##__VA_ARGS__); } while(0) From 8ecd78cbca4cae0f5f73793239915b772442ea5c Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 16:13:39 +0300 Subject: [PATCH 50/66] LOG_ISR_ERROR on xEventGroupSetBitsFromISR failure --- components/atx_psu/atx_psu.c | 4 +++- components/i2s_out/esp32/intr_iram.c | 2 ++ components/i2s_out/esp8266/dma.c | 6 +++++- components/i2s_out/esp8266/i2s.c | 6 +++++- components/user_leds/gpio.c | 4 +++- 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/components/atx_psu/atx_psu.c b/components/atx_psu/atx_psu.c index 1d98f26e..4d64ead7 100644 --- a/components/atx_psu/atx_psu.c +++ b/components/atx_psu/atx_psu.c @@ -1,3 +1,5 @@ +#define ERROR + #include #include @@ -61,7 +63,7 @@ static IRAM_ATTR void atx_psu_gpio_interrupt(gpio_pins_t pins, void *arg) LOG_ISR_DEBUG("pins=" GPIO_PINS_FMT, GPIO_PINS_ARGS(pins)); if (!xEventGroupSetBitsFromISR(atx_psu->events, ATX_PSU_EVENTS_INTR_BIT, &xHigherPriorityTaskWoken)) { - LOG_ISR_WARN("xEventGroupSetBitsFromISR"); + LOG_ISR_ERROR("xEventGroupSetBitsFromISR"); } #if CONFIG_IDF_TARGET_ESP8266 diff --git a/components/i2s_out/esp32/intr_iram.c b/components/i2s_out/esp32/intr_iram.c index 6a4dcb51..17778cfb 100644 --- a/components/i2s_out/esp32/intr_iram.c +++ b/components/i2s_out/esp32/intr_iram.c @@ -1,3 +1,5 @@ +#define ERROR + #include "../i2s_out.h" #include "dma.h" #include "intr.h" diff --git a/components/i2s_out/esp8266/dma.c b/components/i2s_out/esp8266/dma.c index 89587ca0..8367fc98 100644 --- a/components/i2s_out/esp8266/dma.c +++ b/components/i2s_out/esp8266/dma.c @@ -1,3 +1,5 @@ +#define ERROR + #include "../i2s_out.h" #include "slc.h" #include "slc_isr.h" @@ -204,7 +206,9 @@ void IRAM_ATTR i2s_out_slc_isr(void *arg) slc_stop(&SLC0); // unblock flush() task - xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, &task_woken); + if (!xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, &task_woken)) { + LOG_ISR_ERROR("xEventGroupSetBitsFromISR") + } } if (SLC0.int_st.rx_dscr_err) { LOG_ISR_WARN("rx_dscr_err"); diff --git a/components/i2s_out/esp8266/i2s.c b/components/i2s_out/esp8266/i2s.c index fa8fe197..3636be80 100644 --- a/components/i2s_out/esp8266/i2s.c +++ b/components/i2s_out/esp8266/i2s.c @@ -1,3 +1,5 @@ +#define ERROR + #include "../i2s_out.h" #include "i2s.h" @@ -24,7 +26,9 @@ static void IRAM_ATTR i2s_isr(void *arg) if (I2S0.int_st.tx_rempty) { // unblock flush() task - xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF, &task_woken); + if (!xEventGroupSetBitsFromISR(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF, &task_woken)) { + LOG_ISR_ERROR("xEventGroupSetBitsFromISR"); + } // interrupt will fire until disabled I2S0.int_ena.tx_rempty = 0; diff --git a/components/user_leds/gpio.c b/components/user_leds/gpio.c index 84f2ee49..c5413de8 100644 --- a/components/user_leds/gpio.c +++ b/components/user_leds/gpio.c @@ -1,3 +1,5 @@ +#define ERROR + #include #include "user_leds.h" @@ -28,7 +30,7 @@ static IRAM_ATTR void user_leds_gpio_interrupt(gpio_pins_t pins, void *arg) } if (!xEventGroupSetBitsFromISR(leds->event_group, USER_LEDS_EVENT_BIT(led->index), &xHigherPriorityTaskWoken)) { - LOG_ISR_WARN("xEventGroupSetBitsFromISR"); + LOG_ISR_ERROR("xEventGroupSetBitsFromISR"); } } From 00375a229f301ec87d0327d0b3540754613c5760 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 16:28:37 +0300 Subject: [PATCH 51/66] i2s_out: replace DMA_DONE event group bit with task notification --- components/i2s_out/esp32/dma.c | 39 +++++++++++++++++++++++----- components/i2s_out/esp32/intr_iram.c | 16 ++++++++---- components/i2s_out/i2s_out.h | 5 ++++ 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index fcf27408..0a2312a9 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -200,6 +200,8 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt i2s_out->dma_start = false; i2s_out->dma_write_desc = i2s_out->dma_out_desc; i2s_out->dma_eof_desc = NULL; + i2s_out->dma_done = false; + i2s_out->dma_done_task = NULL; return 0; } @@ -487,6 +489,8 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_DONE); i2s_out->dma_eof_desc = NULL; + i2s_out->dma_done = false; + i2s_out->dma_done_task = NULL; taskENTER_CRITICAL(&i2s_out->mux); @@ -518,24 +522,43 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) int i2s_out_dma_flush(struct i2s_out *i2s_out, TickType_t timeout) { + bool dma_done = false; + uint32_t bits = 0; + LOG_DEBUG("..."); - EventBits_t bits = xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_DONE, false, false, timeout); + taskENTER_CRITICAL(&i2s_out->mux); + + dma_done = i2s_out->dma_done; + + if (!dma_done) { + i2s_out->dma_done_task = xTaskGetCurrentTaskHandle(); + } + + taskEXIT_CRITICAL(&i2s_out->mux); + + if (dma_done) { + LOG_DEBUG("done"); - if (!(bits & I2S_OUT_EVENT_GROUP_BIT_DMA_DONE)) { - LOG_ERROR("timeout -> bits=%08x, dma_out_desc=%p...%p dma_write_desc=%p dma_end_desc=%p dma_eof_desc=%p", + return 0; + } else if (!xTaskNotifyWait(0, I2S_OUT_TASK_NOTIFY_BIT_DMA_DONE, &bits, timeout)) { + LOG_ERROR("timeout -> bits=%08x, dma_out_desc=%p...%p dma_write_desc=%p dma_eof_desc=%p dma_end_desc=%p dma_done=%d", bits, i2s_out->dma_out_desc, i2s_out->dma_out_desc + i2s_out->dma_out_count - 1, i2s_out->dma_write_desc, + i2s_out->dma_eof_desc, i2s_out->dma_end_desc, - i2s_out->dma_eof_desc + i2s_out->dma_done ); + + i2s_out->dma_done_task = NULL; + return -1; } else { - LOG_DEBUG("wait -> bits=%08x", bits); - } + LOG_DEBUG("wait -> done"); - return 0; + return 0; + } } void i2s_out_dma_stop(struct i2s_out *i2s_out) @@ -564,6 +587,8 @@ void i2s_out_dma_stop(struct i2s_out *i2s_out) xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_DONE); i2s_out->dma_eof_desc = NULL; + i2s_out->dma_done = false; + i2s_out->dma_done_task = NULL; } void i2s_out_dma_free(struct i2s_out *i2s_out) diff --git a/components/i2s_out/esp32/intr_iram.c b/components/i2s_out/esp32/intr_iram.c index 17778cfb..2fd141f7 100644 --- a/components/i2s_out/esp32/intr_iram.c +++ b/components/i2s_out/esp32/intr_iram.c @@ -18,7 +18,7 @@ EventBits_t i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out) return 0; } -EventBits_t i2s_intr_out_eof_handler(struct i2s_out *i2s_out) +EventBits_t i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *pxHigherPriorityTaskWoken) { uint32_t eof_addr; @@ -67,8 +67,14 @@ EventBits_t i2s_intr_out_eof_handler(struct i2s_out *i2s_out) } i2s_out->dma_eof_desc = eof_desc; + + // unblock flush() i2s_out->dma_done = true; + if (i2s_out->dma_done_task) { + xTaskNotifyFromISR(i2s_out->dma_done_task, I2S_OUT_TASK_NOTIFY_BIT_DMA_DONE, eSetBits, pxHigherPriorityTaskWoken); + } + // unblock get(), flush() task return I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_DONE; @@ -95,7 +101,7 @@ void i2s_intr_handler(void *arg) struct i2s_out *i2s_out = arg; uint32_t int_st = i2s_ll_get_intr_status(i2s_out->dev); EventBits_t event_bits = 0; - BaseType_t task_woken = pdFALSE; + BaseType_t pxHigherPriorityTaskWoken = pdFALSE; if (!int_st) { return; @@ -107,7 +113,7 @@ void i2s_intr_handler(void *arg) event_bits |= i2s_intr_out_dscr_err_handler(i2s_out); } if (int_st & I2S_OUT_EOF_INT_ST) { - event_bits |= i2s_intr_out_eof_handler(i2s_out); + event_bits |= i2s_intr_out_eof_handler(i2s_out, &pxHigherPriorityTaskWoken); } if (int_st & I2S_TX_REMPTY_INT_ST) { event_bits |= i2s_intr_tx_rempty_handler(i2s_out); @@ -115,14 +121,14 @@ void i2s_intr_handler(void *arg) if (event_bits) { // XXX: loops via the timer daemon task, replace with a semaphore? - if (xEventGroupSetBitsFromISR(i2s_out->event_group, event_bits, &task_woken) == pdFALSE) { + if (xEventGroupSetBitsFromISR(i2s_out->event_group, event_bits, &pxHigherPriorityTaskWoken) == pdFALSE) { LOG_ISR_ERROR("xEventGroupSetBitsFromISR"); } } taskEXIT_CRITICAL_ISR(&i2s_out->mux); - if (task_woken == pdTRUE) { + if (pxHigherPriorityTaskWoken == pdTRUE) { portYIELD_FROM_ISR(); } } diff --git a/components/i2s_out/i2s_out.h b/components/i2s_out/i2s_out.h index 7f9d153a..598cfbe4 100644 --- a/components/i2s_out/i2s_out.h +++ b/components/i2s_out/i2s_out.h @@ -12,6 +12,8 @@ struct dma_desc; +#define I2S_OUT_TASK_NOTIFY_BIT_DMA_DONE (1 << 1) + #define I2S_OUT_EVENT_GROUP_BIT_DMA_EOF (1 << 0) #define I2S_OUT_EVENT_GROUP_BIT_DMA_DONE (1 << 1) #define I2S_OUT_EVENT_GROUP_BIT_I2S_EOF (1 << 2) @@ -56,6 +58,9 @@ struct i2s_out { struct dma_desc *dma_eof_desc; bool dma_start; // initialized by i2s_out_dma_setup(), set by i2s_out_dma_start(), cleared by i2s_out_dma_stop() + bool dma_done; + + TaskHandle_t dma_done_task; }; /* dma.c */ From 89f8812c57a4e729213d7cc4c80c804e071b95f6 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 16:45:00 +0300 Subject: [PATCH 52/66] i2s_out: replace DMA_EOF event group bit with task notification --- components/i2s_out/esp32/dma.c | 40 +++++++++++++++++++++++----- components/i2s_out/esp32/intr_iram.c | 9 +++++++ components/i2s_out/i2s_out.h | 4 ++- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 0a2312a9..9b7cfeb2 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -200,6 +200,7 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt i2s_out->dma_start = false; i2s_out->dma_write_desc = i2s_out->dma_out_desc; i2s_out->dma_eof_desc = NULL; + i2s_out->dma_eof_task = NULL; i2s_out->dma_done = false; i2s_out->dma_done_task = NULL; @@ -212,16 +213,41 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt struct dma_desc *i2s_out_dma_wait(struct i2s_out *i2s_out, TickType_t timeout) { if (i2s_out->dma_start) { - while (i2s_out->dma_write_desc->owner) { - LOG_DEBUG("wait for dma_eof_desc=%p vs dma_write_desc=%p, dma_end_desc=%p", i2s_out->dma_eof_desc, i2s_out->dma_write_desc, i2s_out->dma_end_desc); + do { + uint32_t bits; + bool dma_write_owner; - EventBits_t bits = xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF, true, true, timeout); + taskENTER_CRITICAL(&i2s_out->mux); + + if ((dma_write_owner = i2s_out->dma_write_desc->owner)) { + i2s_out->dma_eof_task = xTaskGetCurrentTaskHandle(); + } + + taskEXIT_CRITICAL(&i2s_out->mux); + + if (!dma_write_owner) { + LOG_DEBUG("done"); + + break; + } else if (!xTaskNotifyWait(0, I2S_OUT_TASK_NOTIFY_BIT_DMA_EOF, &bits, timeout)) { + LOG_ERROR("timeout -> bits=%08x, dma_out_desc=%p...%p dma_write_desc=%p dma_eof_desc=%p dma_end_desc=%p dma_done=%d", + bits, + i2s_out->dma_out_desc, i2s_out->dma_out_desc + i2s_out->dma_out_count - 1, + i2s_out->dma_write_desc, + i2s_out->dma_eof_desc, + i2s_out->dma_end_desc, + i2s_out->dma_done + ); + + i2s_out->dma_eof_task = NULL; - if (!(bits & I2S_OUT_EVENT_GROUP_BIT_DMA_EOF)) { - LOG_WARN("timeout -> bits=%08x", bits); return NULL; + } else { + LOG_DEBUG("wait -> done"); + + break; } - } + } while (1); } if (i2s_out->dma_write_desc->owner) { @@ -489,6 +515,7 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_DONE); i2s_out->dma_eof_desc = NULL; + i2s_out->dma_eof_task = NULL; i2s_out->dma_done = false; i2s_out->dma_done_task = NULL; @@ -587,6 +614,7 @@ void i2s_out_dma_stop(struct i2s_out *i2s_out) xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_DONE); i2s_out->dma_eof_desc = NULL; + i2s_out->dma_eof_task = NULL; i2s_out->dma_done = false; i2s_out->dma_done_task = NULL; } diff --git a/components/i2s_out/esp32/intr_iram.c b/components/i2s_out/esp32/intr_iram.c index 2fd141f7..d0947007 100644 --- a/components/i2s_out/esp32/intr_iram.c +++ b/components/i2s_out/esp32/intr_iram.c @@ -66,13 +66,22 @@ EventBits_t i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *pxHigh i2s_dma_desc_reset(desc); } + // unblock wait() i2s_out->dma_eof_desc = eof_desc; + if (i2s_out->dma_eof_task) { + xTaskNotifyFromISR(i2s_out->dma_eof_task, I2S_OUT_TASK_NOTIFY_BIT_DMA_EOF, eSetBits, pxHigherPriorityTaskWoken); + + i2s_out->dma_eof_task = NULL; + } + // unblock flush() i2s_out->dma_done = true; if (i2s_out->dma_done_task) { xTaskNotifyFromISR(i2s_out->dma_done_task, I2S_OUT_TASK_NOTIFY_BIT_DMA_DONE, eSetBits, pxHigherPriorityTaskWoken); + + i2s_out->dma_done_task = NULL; } // unblock get(), flush() task diff --git a/components/i2s_out/i2s_out.h b/components/i2s_out/i2s_out.h index 598cfbe4..cbe05bb7 100644 --- a/components/i2s_out/i2s_out.h +++ b/components/i2s_out/i2s_out.h @@ -12,6 +12,7 @@ struct dma_desc; +#define I2S_OUT_TASK_NOTIFY_BIT_DMA_EOF (1 << 0) #define I2S_OUT_TASK_NOTIFY_BIT_DMA_DONE (1 << 1) #define I2S_OUT_EVENT_GROUP_BIT_DMA_EOF (1 << 0) @@ -60,7 +61,8 @@ struct i2s_out { bool dma_start; // initialized by i2s_out_dma_setup(), set by i2s_out_dma_start(), cleared by i2s_out_dma_stop() bool dma_done; - TaskHandle_t dma_done_task; + TaskHandle_t dma_eof_task; // task waiting for dma_eof_desc to update + TaskHandle_t dma_done_task; // task waiting for dma_done to be set }; /* dma.c */ From 76b6ad83db0de0c3bd405600a01bab1d13a07424 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 16:45:13 +0300 Subject: [PATCH 53/66] i2s_out: replace I2S_DONE event group bit with task notification --- components/i2s_out/esp32/i2s.c | 53 +++++++++++++++++++--------- components/i2s_out/esp32/intr_iram.c | 13 +++++-- components/i2s_out/i2s_out.h | 3 ++ 3 files changed, 50 insertions(+), 19 deletions(-) diff --git a/components/i2s_out/esp32/i2s.c b/components/i2s_out/esp32/i2s.c index 7193b2ff..2e885dba 100644 --- a/components/i2s_out/esp32/i2s.c +++ b/components/i2s_out/esp32/i2s.c @@ -119,6 +119,9 @@ int i2s_out_i2s_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt // reset event state xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF); + i2s_out->i2s_done = false; + i2s_out->i2s_done_task = NULL; + return 0; } @@ -129,6 +132,9 @@ void i2s_out_i2s_start(struct i2s_out *i2s_out) // reset event state xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF); + i2s_out->i2s_done = false; + i2s_out->i2s_done_task = NULL; + taskENTER_CRITICAL(&i2s_out->mux); // NOTE: there seems to always be three extra BCK cycles at the start of TX @@ -142,32 +148,42 @@ void i2s_out_i2s_start(struct i2s_out *i2s_out) int i2s_out_i2s_flush(struct i2s_out *i2s_out, TickType_t timeout) { - EventBits_t bits; + bool i2s_done = false; + uint32_t bits = 0; - if ((bits = xEventGroupGetBits(i2s_out->event_group)) & I2S_OUT_EVENT_GROUP_BIT_I2S_EOF) { - // TX_REMPTY has already occured - LOG_DEBUG("skip event_group bits=%08x", bits); - } else { - taskENTER_CRITICAL(&i2s_out->mux); + LOG_DEBUG("..."); + + taskENTER_CRITICAL(&i2s_out->mux); + + i2s_done = i2s_out->i2s_done; + + if (!i2s_done) { + i2s_out->i2s_done_task = xTaskGetCurrentTaskHandle(); i2s_intr_clear(i2s_out->dev, I2S_TX_REMPTY_INT_CLR); i2s_intr_enable(i2s_out->dev, I2S_TX_REMPTY_INT_ENA); + } - taskEXIT_CRITICAL(&i2s_out->mux); + taskEXIT_CRITICAL(&i2s_out->mux); - LOG_DEBUG("..."); + if (i2s_done) { + LOG_DEBUG("done"); - bits = xEventGroupWaitBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF, false, false, timeout); + return 0; + } else if (!xTaskNotifyWait(0, I2S_OUT_TASK_NOTIFY_BIT_I2S_DONE, &bits, timeout)) { + LOG_ERROR("timeout -> bits=%08x i2s_done=%d", + bits, + i2s_out->i2s_done + ); - if (!(bits & I2S_OUT_EVENT_GROUP_BIT_I2S_EOF)) { - LOG_ERROR("timeout -> bits=%08x", bits); - return -1; - } else { - LOG_DEBUG("wait -> bits=%08x", bits); - } - } + i2s_out->i2s_done_task = NULL; - return 0; + return -1; + } else { + LOG_DEBUG("wait -> done"); + + return 0; + } } void i2s_out_i2s_stop(struct i2s_out *i2s_out) @@ -182,4 +198,7 @@ void i2s_out_i2s_stop(struct i2s_out *i2s_out) // reset event state xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF); + + i2s_out->i2s_done = false; + i2s_out->i2s_done_task = NULL; } diff --git a/components/i2s_out/esp32/intr_iram.c b/components/i2s_out/esp32/intr_iram.c index d0947007..d1a7efaf 100644 --- a/components/i2s_out/esp32/intr_iram.c +++ b/components/i2s_out/esp32/intr_iram.c @@ -94,7 +94,7 @@ EventBits_t i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *pxHigh } } -EventBits_t i2s_intr_tx_rempty_handler(struct i2s_out *i2s_out) +EventBits_t i2s_intr_tx_rempty_handler(struct i2s_out *i2s_out, BaseType_t *pxHigherPriorityTaskWoken) { LOG_ISR_DEBUG(""); @@ -102,6 +102,15 @@ EventBits_t i2s_intr_tx_rempty_handler(struct i2s_out *i2s_out) i2s_intr_disable(i2s_out->dev, I2S_TX_REMPTY_INT_ENA); i2s_intr_clear(i2s_out->dev, I2S_TX_REMPTY_INT_CLR); + // unblock flush() task + i2s_out->i2s_done = true; + + if (i2s_out->i2s_done_task) { + xTaskNotifyFromISR(i2s_out->i2s_done_task, I2S_OUT_TASK_NOTIFY_BIT_I2S_DONE, eSetBits, pxHigherPriorityTaskWoken); + + i2s_out->i2s_done_task = NULL; + } + return I2S_OUT_EVENT_GROUP_BIT_I2S_EOF; } @@ -125,7 +134,7 @@ void i2s_intr_handler(void *arg) event_bits |= i2s_intr_out_eof_handler(i2s_out, &pxHigherPriorityTaskWoken); } if (int_st & I2S_TX_REMPTY_INT_ST) { - event_bits |= i2s_intr_tx_rempty_handler(i2s_out); + event_bits |= i2s_intr_tx_rempty_handler(i2s_out, &pxHigherPriorityTaskWoken); } if (event_bits) { diff --git a/components/i2s_out/i2s_out.h b/components/i2s_out/i2s_out.h index cbe05bb7..d22f67a2 100644 --- a/components/i2s_out/i2s_out.h +++ b/components/i2s_out/i2s_out.h @@ -14,6 +14,7 @@ struct dma_desc; #define I2S_OUT_TASK_NOTIFY_BIT_DMA_EOF (1 << 0) #define I2S_OUT_TASK_NOTIFY_BIT_DMA_DONE (1 << 1) +#define I2S_OUT_TASK_NOTIFY_BIT_I2S_DONE (1 << 2) #define I2S_OUT_EVENT_GROUP_BIT_DMA_EOF (1 << 0) #define I2S_OUT_EVENT_GROUP_BIT_DMA_DONE (1 << 1) @@ -60,9 +61,11 @@ struct i2s_out { bool dma_start; // initialized by i2s_out_dma_setup(), set by i2s_out_dma_start(), cleared by i2s_out_dma_stop() bool dma_done; + bool i2s_done; TaskHandle_t dma_eof_task; // task waiting for dma_eof_desc to update TaskHandle_t dma_done_task; // task waiting for dma_done to be set + TaskHandle_t i2s_done_task; // task waiting for i2s_done to be set }; /* dma.c */ From ff0b87543f0b3d60c9aa0b290956ae7a6d0c3370 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 23 May 2026 16:50:10 +0300 Subject: [PATCH 54/66] i2s_out: remove event_group on esp32 --- components/i2s_out/esp32/dma.c | 11 ++------ components/i2s_out/esp32/i2s.c | 6 ----- components/i2s_out/esp32/intr_iram.c | 39 +++++++++------------------- components/i2s_out/i2s_out.c | 2 ++ components/i2s_out/i2s_out.h | 18 ++++++++----- 5 files changed, 28 insertions(+), 48 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 9b7cfeb2..7f145939 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -193,10 +193,7 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt taskEXIT_CRITICAL(&i2s_out->mux); - // reset eof state - xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_DONE); - - // reset write state + // reset eof/write state i2s_out->dma_start = false; i2s_out->dma_write_desc = i2s_out->dma_out_desc; i2s_out->dma_eof_desc = NULL; @@ -511,9 +508,7 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) i2s_out->dma_end_desc->next ); - // reset eof state - xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_DONE); - + // reset eof/done state i2s_out->dma_eof_desc = NULL; i2s_out->dma_eof_task = NULL; i2s_out->dma_done = false; @@ -611,8 +606,6 @@ void i2s_out_dma_stop(struct i2s_out *i2s_out) taskEXIT_CRITICAL(&i2s_out->mux); // reset eof state - xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_DONE); - i2s_out->dma_eof_desc = NULL; i2s_out->dma_eof_task = NULL; i2s_out->dma_done = false; diff --git a/components/i2s_out/esp32/i2s.c b/components/i2s_out/esp32/i2s.c index 2e885dba..aafe41c5 100644 --- a/components/i2s_out/esp32/i2s.c +++ b/components/i2s_out/esp32/i2s.c @@ -117,8 +117,6 @@ int i2s_out_i2s_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt ); // reset event state - xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF); - i2s_out->i2s_done = false; i2s_out->i2s_done_task = NULL; @@ -130,8 +128,6 @@ void i2s_out_i2s_start(struct i2s_out *i2s_out) LOG_DEBUG(""); // reset event state - xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF); - i2s_out->i2s_done = false; i2s_out->i2s_done_task = NULL; @@ -197,8 +193,6 @@ void i2s_out_i2s_stop(struct i2s_out *i2s_out) taskEXIT_CRITICAL(&i2s_out->mux); // reset event state - xEventGroupClearBits(i2s_out->event_group, I2S_OUT_EVENT_GROUP_BIT_I2S_EOF); - i2s_out->i2s_done = false; i2s_out->i2s_done_task = NULL; } diff --git a/components/i2s_out/esp32/intr_iram.c b/components/i2s_out/esp32/intr_iram.c index d1a7efaf..e7d9a3c6 100644 --- a/components/i2s_out/esp32/intr_iram.c +++ b/components/i2s_out/esp32/intr_iram.c @@ -6,7 +6,7 @@ #include -EventBits_t i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out) +void i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out) { uint32_t dscr_addr; @@ -14,11 +14,9 @@ EventBits_t i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out) i2s_dma_tx_get_des_addr(i2s_out->dev, &dscr_addr); LOG_ISR_ERROR("desc=%p", dscr_addr); - - return 0; } -EventBits_t i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *pxHigherPriorityTaskWoken) +void i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *pxHigherPriorityTaskWoken) { uint32_t eof_addr; @@ -46,16 +44,18 @@ EventBits_t i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *pxHigh i2s_dma_desc_reset(eof_desc); } + // unblock wait() i2s_out->dma_eof_desc = eof_desc; - // unblock get() task - return I2S_OUT_EVENT_GROUP_BIT_DMA_EOF; + if (i2s_out->dma_eof_task) { + xTaskNotifyFromISR(i2s_out->dma_eof_task, I2S_OUT_TASK_NOTIFY_BIT_DMA_EOF, eSetBits, pxHigherPriorityTaskWoken); + + i2s_out->dma_eof_task = NULL; + } } else if (i2s_out->dma_repeat_desc && eof_desc >= i2s_out->dma_repeat_desc && eof_desc < i2s_out->dma_repeat_desc + i2s_out->dma_out_count * i2s_out->dma_repeat_count) { LOG_ISR_DEBUG("ignore repeat desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); - return 0; - } else if (eof_desc == i2s_out->dma_end_desc) { // we may miss some EOF ISR for intermediate DMA descriptors, unblock i2s_out_dma_wait() LOG_ISR_WARN("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); @@ -84,17 +84,12 @@ EventBits_t i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *pxHigh i2s_out->dma_done_task = NULL; } - // unblock get(), flush() task - return I2S_OUT_EVENT_GROUP_BIT_DMA_EOF | I2S_OUT_EVENT_GROUP_BIT_DMA_DONE; - } else { LOG_ISR_ERROR("ignore desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); - - return 0; } } -EventBits_t i2s_intr_tx_rempty_handler(struct i2s_out *i2s_out, BaseType_t *pxHigherPriorityTaskWoken) +void i2s_intr_tx_rempty_handler(struct i2s_out *i2s_out, BaseType_t *pxHigherPriorityTaskWoken) { LOG_ISR_DEBUG(""); @@ -110,15 +105,12 @@ EventBits_t i2s_intr_tx_rempty_handler(struct i2s_out *i2s_out, BaseType_t *pxHi i2s_out->i2s_done_task = NULL; } - - return I2S_OUT_EVENT_GROUP_BIT_I2S_EOF; } void i2s_intr_handler(void *arg) { struct i2s_out *i2s_out = arg; uint32_t int_st = i2s_ll_get_intr_status(i2s_out->dev); - EventBits_t event_bits = 0; BaseType_t pxHigherPriorityTaskWoken = pdFALSE; if (!int_st) { @@ -128,20 +120,13 @@ void i2s_intr_handler(void *arg) taskENTER_CRITICAL_ISR(&i2s_out->mux); if (int_st & I2S_OUT_DSCR_ERR_INT_ST) { - event_bits |= i2s_intr_out_dscr_err_handler(i2s_out); + i2s_intr_out_dscr_err_handler(i2s_out); } if (int_st & I2S_OUT_EOF_INT_ST) { - event_bits |= i2s_intr_out_eof_handler(i2s_out, &pxHigherPriorityTaskWoken); + i2s_intr_out_eof_handler(i2s_out, &pxHigherPriorityTaskWoken); } if (int_st & I2S_TX_REMPTY_INT_ST) { - event_bits |= i2s_intr_tx_rempty_handler(i2s_out, &pxHigherPriorityTaskWoken); - } - - if (event_bits) { - // XXX: loops via the timer daemon task, replace with a semaphore? - if (xEventGroupSetBitsFromISR(i2s_out->event_group, event_bits, &pxHigherPriorityTaskWoken) == pdFALSE) { - LOG_ISR_ERROR("xEventGroupSetBitsFromISR"); - } + i2s_intr_tx_rempty_handler(i2s_out, &pxHigherPriorityTaskWoken); } taskEXIT_CRITICAL_ISR(&i2s_out->mux); diff --git a/components/i2s_out/i2s_out.c b/components/i2s_out/i2s_out.c index 6dedc92d..992cbcdb 100644 --- a/components/i2s_out/i2s_out.c +++ b/components/i2s_out/i2s_out.c @@ -21,10 +21,12 @@ int i2s_out_init(struct i2s_out *i2s_out, i2s_port_t port) return -1; } +#if CONFIG_IDF_TARGET_ESP8266 if (!(i2s_out->event_group = xEventGroupCreate())) { LOG_ERROR("xEventGroupCreate"); return -1; } +#endif #if CONFIG_IDF_TARGET_ESP32 portMUX_INITIALIZE(&i2s_out->mux); diff --git a/components/i2s_out/i2s_out.h b/components/i2s_out/i2s_out.h index d22f67a2..6b9e499f 100644 --- a/components/i2s_out/i2s_out.h +++ b/components/i2s_out/i2s_out.h @@ -12,13 +12,17 @@ struct dma_desc; -#define I2S_OUT_TASK_NOTIFY_BIT_DMA_EOF (1 << 0) -#define I2S_OUT_TASK_NOTIFY_BIT_DMA_DONE (1 << 1) -#define I2S_OUT_TASK_NOTIFY_BIT_I2S_DONE (1 << 2) +#if CONFIG_IDF_TARGET_ESP32 + #define I2S_OUT_TASK_NOTIFY_BIT_DMA_EOF (1 << 0) + #define I2S_OUT_TASK_NOTIFY_BIT_DMA_DONE (1 << 1) + #define I2S_OUT_TASK_NOTIFY_BIT_I2S_DONE (1 << 2) +#endif -#define I2S_OUT_EVENT_GROUP_BIT_DMA_EOF (1 << 0) -#define I2S_OUT_EVENT_GROUP_BIT_DMA_DONE (1 << 1) -#define I2S_OUT_EVENT_GROUP_BIT_I2S_EOF (1 << 2) +#if CONFIG_IDF_TARGET_ESP8266 + #define I2S_OUT_EVENT_GROUP_BIT_DMA_EOF (1 << 0) + #define I2S_OUT_EVENT_GROUP_BIT_DMA_DONE (1 << 1) + #define I2S_OUT_EVENT_GROUP_BIT_I2S_EOF (1 << 2) +#endif struct i2s_out { i2s_port_t port; @@ -26,7 +30,9 @@ struct i2s_out { #if CONFIG_IDF_TARGET_ESP32 portMUX_TYPE mux; #endif +#if CONFIG_IDF_TARGET_ESP8266 EventGroupHandle_t event_group; +#endif bool setup; /* dev */ From dd689829ee4034d444213d879a98ff7e80d0a63d Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Fri, 5 Jun 2026 12:16:54 +0300 Subject: [PATCH 55/66] i2s_out: refactor intr state set --- components/i2s_out/esp32/intr_iram.c | 88 ++++++++++++++-------------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/components/i2s_out/esp32/intr_iram.c b/components/i2s_out/esp32/intr_iram.c index e7d9a3c6..54567576 100644 --- a/components/i2s_out/esp32/intr_iram.c +++ b/components/i2s_out/esp32/intr_iram.c @@ -6,7 +6,44 @@ #include -void i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out) +// we may miss some EOF ISR for intermediate DMA descriptors, ensure we unblock i2s_out_dma_wait() on owner up to eof_desc +static void i2s_out_intr_dma_eof_desc(struct i2s_out *i2s_out, struct dma_desc *eof_desc, BaseType_t *pxHigherPriorityTaskWoken) +{ + if (i2s_out->dma_eof_desc) { + for (struct dma_desc *desc = eof_desc; desc > i2s_out->dma_eof_desc; desc--) { + if (desc != eof_desc) { + LOG_ISR_WARN("miss desc=%p owner=%u len=%u", desc, desc->owner, desc->len); + } + + i2s_dma_desc_reset(desc); + } + } else { + i2s_dma_desc_reset(eof_desc); + } + + // unblock wait() + i2s_out->dma_eof_desc = eof_desc; + + if (i2s_out->dma_eof_task) { + xTaskNotifyFromISR(i2s_out->dma_eof_task, I2S_OUT_TASK_NOTIFY_BIT_DMA_EOF, eSetBits, pxHigherPriorityTaskWoken); + + i2s_out->dma_eof_task = NULL; + } +} + +static void i2s_out_intr_dma_done(struct i2s_out *i2s_out, BaseType_t *pxHigherPriorityTaskWoken) +{ + // unblock flush() + i2s_out->dma_done = true; + + if (i2s_out->dma_done_task) { + xTaskNotifyFromISR(i2s_out->dma_done_task, I2S_OUT_TASK_NOTIFY_BIT_DMA_DONE, eSetBits, pxHigherPriorityTaskWoken); + + i2s_out->dma_done_task = NULL; + } +} + +static void i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out) { uint32_t dscr_addr; @@ -16,7 +53,7 @@ void i2s_intr_out_dscr_err_handler(struct i2s_out *i2s_out) LOG_ISR_ERROR("desc=%p", dscr_addr); } -void i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *pxHigherPriorityTaskWoken) +static void i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *pxHigherPriorityTaskWoken) { uint32_t eof_addr; @@ -31,27 +68,7 @@ void i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *pxHigherPrior if (eof_desc >= i2s_out->dma_out_desc && eof_desc < i2s_out->dma_out_desc + i2s_out->dma_out_count) { LOG_ISR_DEBUG("eof desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); - // we may miss some EOF ISR for intermediate DMA descriptors, unblock i2s_out_dma_wait() - if (i2s_out->dma_eof_desc) { - for (struct dma_desc *desc = eof_desc; desc > i2s_out->dma_eof_desc; desc--) { - if (desc != eof_desc) { - LOG_ISR_WARN("miss desc=%p owner=%u len=%u", desc, desc->owner, desc->len); - } - - i2s_dma_desc_reset(desc); - } - } else { - i2s_dma_desc_reset(eof_desc); - } - - // unblock wait() - i2s_out->dma_eof_desc = eof_desc; - - if (i2s_out->dma_eof_task) { - xTaskNotifyFromISR(i2s_out->dma_eof_task, I2S_OUT_TASK_NOTIFY_BIT_DMA_EOF, eSetBits, pxHigherPriorityTaskWoken); - - i2s_out->dma_eof_task = NULL; - } + i2s_out_intr_dma_eof_desc(i2s_out, eof_desc, pxHigherPriorityTaskWoken); } else if (i2s_out->dma_repeat_desc && eof_desc >= i2s_out->dma_repeat_desc && eof_desc < i2s_out->dma_repeat_desc + i2s_out->dma_out_count * i2s_out->dma_repeat_count) { LOG_ISR_DEBUG("ignore repeat desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); @@ -61,35 +78,16 @@ void i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *pxHigherPrior LOG_ISR_WARN("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); eof_desc = i2s_out->dma_out_desc + i2s_out->dma_out_count - 1; - - for (struct dma_desc *desc = eof_desc; desc > i2s_out->dma_eof_desc; desc--) { - i2s_dma_desc_reset(desc); - } - - // unblock wait() - i2s_out->dma_eof_desc = eof_desc; - if (i2s_out->dma_eof_task) { - xTaskNotifyFromISR(i2s_out->dma_eof_task, I2S_OUT_TASK_NOTIFY_BIT_DMA_EOF, eSetBits, pxHigherPriorityTaskWoken); - - i2s_out->dma_eof_task = NULL; - } - - // unblock flush() - i2s_out->dma_done = true; - - if (i2s_out->dma_done_task) { - xTaskNotifyFromISR(i2s_out->dma_done_task, I2S_OUT_TASK_NOTIFY_BIT_DMA_DONE, eSetBits, pxHigherPriorityTaskWoken); - - i2s_out->dma_done_task = NULL; - } + i2s_out_intr_dma_eof_desc(i2s_out, eof_desc, pxHigherPriorityTaskWoken); + i2s_out_intr_dma_done(i2s_out, pxHigherPriorityTaskWoken); } else { LOG_ISR_ERROR("ignore desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); } } -void i2s_intr_tx_rempty_handler(struct i2s_out *i2s_out, BaseType_t *pxHigherPriorityTaskWoken) +static void i2s_intr_tx_rempty_handler(struct i2s_out *i2s_out, BaseType_t *pxHigherPriorityTaskWoken) { LOG_ISR_DEBUG(""); From 4205fbf6d6de2d8ffb8a164a7c7ab8f0e823aa3a Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Fri, 5 Jun 2026 12:26:02 +0300 Subject: [PATCH 56/66] i2s_out: fix missed first eof desc --- components/i2s_out/esp32/intr_iram.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/components/i2s_out/esp32/intr_iram.c b/components/i2s_out/esp32/intr_iram.c index 54567576..67371792 100644 --- a/components/i2s_out/esp32/intr_iram.c +++ b/components/i2s_out/esp32/intr_iram.c @@ -9,6 +9,7 @@ // we may miss some EOF ISR for intermediate DMA descriptors, ensure we unblock i2s_out_dma_wait() on owner up to eof_desc static void i2s_out_intr_dma_eof_desc(struct i2s_out *i2s_out, struct dma_desc *eof_desc, BaseType_t *pxHigherPriorityTaskWoken) { + // update dma_eof_desc if (i2s_out->dma_eof_desc) { for (struct dma_desc *desc = eof_desc; desc > i2s_out->dma_eof_desc; desc--) { if (desc != eof_desc) { @@ -18,12 +19,18 @@ static void i2s_out_intr_dma_eof_desc(struct i2s_out *i2s_out, struct dma_desc * i2s_dma_desc_reset(desc); } } else { - i2s_dma_desc_reset(eof_desc); + for (struct dma_desc *desc = eof_desc; desc >= i2s_out->dma_out_desc; desc--) { + if (desc != eof_desc) { + LOG_ISR_WARN("miss desc=%p owner=%u len=%u", desc, desc->owner, desc->len); + } + + i2s_dma_desc_reset(desc); + } } - // unblock wait() i2s_out->dma_eof_desc = eof_desc; - + + // unblock wait() if (i2s_out->dma_eof_task) { xTaskNotifyFromISR(i2s_out->dma_eof_task, I2S_OUT_TASK_NOTIFY_BIT_DMA_EOF, eSetBits, pxHigherPriorityTaskWoken); From 2139345c2319c94acaaf6ff5d283c96035a55396 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Fri, 5 Jun 2026 12:39:34 +0300 Subject: [PATCH 57/66] i2s_out: rename i2s_data_buf/desc/count --- components/i2s_out/esp32/dma.c | 80 ++++++++++++++-------------- components/i2s_out/esp32/intr_iram.c | 8 +-- components/i2s_out/i2s_out.h | 10 ++-- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 7f145939..55fd949a 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -90,11 +90,11 @@ int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align, unsigne LOG_DEBUG("size=%u align=%u repeat=%u -> desc_count=%u buf_size=%u", size, align, repeat, desc_count, buf_size); // allocate single word-aligned buffer - if (!(i2s_out->dma_out_buf = dma_malloc(buf_size))) { - LOG_ERROR("dma_malloc(dma_out_buf)"); + if (!(i2s_out->dma_data_buf = dma_malloc(buf_size))) { + LOG_ERROR("dma_malloc(dma_data_buf)"); return -1; } else { - LOG_DEBUG("dma_out_buf=%p[%u]", i2s_out->dma_out_buf, buf_size); + LOG_DEBUG("dma_data_buf=%p[%u]", i2s_out->dma_data_buf, buf_size); } if (!(i2s_out->dma_end_buf = dma_malloc(DMA_END_BUF_SIZE))) { LOG_ERROR("dma_malloc(dma_end_buf)"); @@ -104,8 +104,8 @@ int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align, unsigne } // allocate DMA descriptors - if (!(i2s_out->dma_out_desc = dma_calloc(desc_count, sizeof(*i2s_out->dma_out_desc)))) { - LOG_ERROR("dma_calloc(dma_out_desc)"); + if (!(i2s_out->dma_data_desc = dma_calloc(desc_count, sizeof(*i2s_out->dma_data_desc)))) { + LOG_ERROR("dma_calloc(dma_data_desc)"); return -1; } if (repeat && !(i2s_out->dma_repeat_desc = dma_calloc(desc_count * repeat, sizeof(*i2s_out->dma_repeat_desc)))) { @@ -118,13 +118,13 @@ int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align, unsigne } // initialize linked list of DMA descriptors - init_dma_desc(i2s_out->dma_out_desc, desc_count, i2s_out->dma_out_buf, buf_size, align, NULL); + init_dma_desc(i2s_out->dma_data_desc, desc_count, i2s_out->dma_data_buf, buf_size, align, NULL); for (unsigned i = 0; i < repeat; i++) { - init_dma_desc(i2s_out->dma_repeat_desc + i * desc_count, desc_count, i2s_out->dma_out_buf, buf_size, align, NULL); + init_dma_desc(i2s_out->dma_repeat_desc + i * desc_count, desc_count, i2s_out->dma_data_buf, buf_size, align, NULL); } init_dma_desc(i2s_out->dma_end_desc, 1, i2s_out->dma_end_buf, DMA_END_BUF_SIZE, sizeof(uint32_t), NULL); - i2s_out->dma_out_count = desc_count; + i2s_out->dma_data_count = desc_count; i2s_out->dma_repeat_count = repeat; return 0; @@ -176,7 +176,7 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt } // reinit out desc init_dma_end(i2s_out->dma_end_desc, options->eof_value, options->eof_count); - reinit_dma_desc(i2s_out->dma_out_desc, i2s_out->dma_out_count, i2s_out->dma_end_desc); + reinit_dma_desc(i2s_out->dma_data_desc, i2s_out->dma_data_count, i2s_out->dma_end_desc); taskENTER_CRITICAL(&i2s_out->mux); @@ -195,7 +195,7 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt // reset eof/write state i2s_out->dma_start = false; - i2s_out->dma_write_desc = i2s_out->dma_out_desc; + i2s_out->dma_write_desc = i2s_out->dma_data_desc; i2s_out->dma_eof_desc = NULL; i2s_out->dma_eof_task = NULL; i2s_out->dma_done = false; @@ -227,9 +227,9 @@ struct dma_desc *i2s_out_dma_wait(struct i2s_out *i2s_out, TickType_t timeout) break; } else if (!xTaskNotifyWait(0, I2S_OUT_TASK_NOTIFY_BIT_DMA_EOF, &bits, timeout)) { - LOG_ERROR("timeout -> bits=%08x, dma_out_desc=%p...%p dma_write_desc=%p dma_eof_desc=%p dma_end_desc=%p dma_done=%d", + LOG_ERROR("timeout -> bits=%08x, dma_data_desc=%p...%p dma_write_desc=%p dma_eof_desc=%p dma_end_desc=%p dma_done=%d", bits, - i2s_out->dma_out_desc, i2s_out->dma_out_desc + i2s_out->dma_out_count - 1, + i2s_out->dma_data_desc, i2s_out->dma_data_desc + i2s_out->dma_data_count - 1, i2s_out->dma_write_desc, i2s_out->dma_eof_desc, i2s_out->dma_end_desc, @@ -277,7 +277,7 @@ struct dma_desc *i2s_out_dma_next(struct i2s_out *i2s_out, TickType_t timeout) // prepare for DMA i2s_out->dma_write_desc->owner = 1; - if (i2s_out->dma_write_desc + 1 >= i2s_out->dma_out_desc + i2s_out->dma_out_count) { + if (i2s_out->dma_write_desc + 1 >= i2s_out->dma_data_desc + i2s_out->dma_data_count) { LOG_WARN("dma_write_desc=%p committed, no more descs available", i2s_out->dma_write_desc); return NULL; } @@ -398,9 +398,9 @@ int i2s_out_dma_repeat(struct i2s_out *i2s_out, unsigned count) LOG_DEBUG("count=%u", count); for (unsigned i = 0; i < count; i++) { - for (unsigned j = 0; j < i2s_out->dma_out_count; j++) { - struct dma_desc *s = &i2s_out->dma_out_desc[j]; - struct dma_desc *d = &i2s_out->dma_repeat_desc[i * i2s_out->dma_out_count + j]; + for (unsigned j = 0; j < i2s_out->dma_data_count; j++) { + struct dma_desc *s = &i2s_out->dma_data_desc[j]; + struct dma_desc *d = &i2s_out->dma_repeat_desc[i * i2s_out->dma_data_count + j]; if (!s->owner) { break; @@ -428,7 +428,7 @@ int i2s_out_dma_repeat(struct i2s_out *i2s_out, unsigned count) int i2s_out_dma_pending(struct i2s_out *i2s_out) { // XXX: this will also be true immediately after dma_start()? - if (i2s_out->dma_write_desc > i2s_out->dma_out_desc || i2s_out->dma_write_desc->len > 0) { + if (i2s_out->dma_write_desc > i2s_out->dma_data_desc || i2s_out->dma_write_desc->len > 0) { // write() happened return 1; } @@ -472,28 +472,28 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) i2s_out->dma_end_desc->owner = 1; i2s_out->dma_end_desc->next = NULL; - for (unsigned i = 0; i < i2s_out->dma_out_count; i++) { - LOG_DEBUG("dma_out_desc[%u]=%p: owner=%d eof=%d len=%u size=%u buf=%p next=%p", i, - &i2s_out->dma_out_desc[i], - i2s_out->dma_out_desc[i].owner, - i2s_out->dma_out_desc[i].eof, - i2s_out->dma_out_desc[i].len, - i2s_out->dma_out_desc[i].size, - i2s_out->dma_out_desc[i].buf, - i2s_out->dma_out_desc[i].next + for (unsigned i = 0; i < i2s_out->dma_data_count; i++) { + LOG_DEBUG("dma_data_desc[%u]=%p: owner=%d eof=%d len=%u size=%u buf=%p next=%p", i, + &i2s_out->dma_data_desc[i], + i2s_out->dma_data_desc[i].owner, + i2s_out->dma_data_desc[i].eof, + i2s_out->dma_data_desc[i].len, + i2s_out->dma_data_desc[i].size, + i2s_out->dma_data_desc[i].buf, + i2s_out->dma_data_desc[i].next ); } for (unsigned i = 0; i < i2s_out->dma_repeat_count; i++) { - for (unsigned j = 0; j < i2s_out->dma_out_count; j++) { + for (unsigned j = 0; j < i2s_out->dma_data_count; j++) { LOG_DEBUG("dma_repeat_desc[%u][%u]=%p: owner=%d eof=%d len=%u size=%u buf=%p next=%p", i, j, - &i2s_out->dma_repeat_desc[i * i2s_out->dma_out_count + j], - i2s_out->dma_repeat_desc[i * i2s_out->dma_out_count + j].owner, - i2s_out->dma_repeat_desc[i * i2s_out->dma_out_count + j].eof, - i2s_out->dma_repeat_desc[i * i2s_out->dma_out_count + j].len, - i2s_out->dma_repeat_desc[i * i2s_out->dma_out_count + j].size, - i2s_out->dma_repeat_desc[i * i2s_out->dma_out_count + j].buf, - i2s_out->dma_repeat_desc[i * i2s_out->dma_out_count + j].next + &i2s_out->dma_repeat_desc[i * i2s_out->dma_data_count + j], + i2s_out->dma_repeat_desc[i * i2s_out->dma_data_count + j].owner, + i2s_out->dma_repeat_desc[i * i2s_out->dma_data_count + j].eof, + i2s_out->dma_repeat_desc[i * i2s_out->dma_data_count + j].len, + i2s_out->dma_repeat_desc[i * i2s_out->dma_data_count + j].size, + i2s_out->dma_repeat_desc[i * i2s_out->dma_data_count + j].buf, + i2s_out->dma_repeat_desc[i * i2s_out->dma_data_count + j].next ); } } @@ -520,7 +520,7 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) i2s_ll_tx_reset_fifo(i2s_out->dev); i2s_ll_tx_reset(i2s_out->dev); - i2s_ll_set_out_link_addr(i2s_out->dev, (uint32_t) i2s_out->dma_out_desc); + i2s_ll_set_out_link_addr(i2s_out->dev, (uint32_t) i2s_out->dma_data_desc); // TODO: use *_burst_en=1 with out_eof_mode=0? i2s_ll_dma_enable_eof_on_fifo_empty(i2s_out->dev, true); @@ -537,7 +537,7 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) // reset state for next write() i2s_out->dma_start = true; - i2s_out->dma_write_desc = i2s_out->dma_out_desc; + i2s_out->dma_write_desc = i2s_out->dma_data_desc; return 0; } @@ -564,9 +564,9 @@ int i2s_out_dma_flush(struct i2s_out *i2s_out, TickType_t timeout) return 0; } else if (!xTaskNotifyWait(0, I2S_OUT_TASK_NOTIFY_BIT_DMA_DONE, &bits, timeout)) { - LOG_ERROR("timeout -> bits=%08x, dma_out_desc=%p...%p dma_write_desc=%p dma_eof_desc=%p dma_end_desc=%p dma_done=%d", + LOG_ERROR("timeout -> bits=%08x, dma_data_desc=%p...%p dma_write_desc=%p dma_eof_desc=%p dma_end_desc=%p dma_done=%d", bits, - i2s_out->dma_out_desc, i2s_out->dma_out_desc + i2s_out->dma_out_count - 1, + i2s_out->dma_data_desc, i2s_out->dma_data_desc + i2s_out->dma_data_count - 1, i2s_out->dma_write_desc, i2s_out->dma_eof_desc, i2s_out->dma_end_desc, @@ -615,8 +615,8 @@ void i2s_out_dma_stop(struct i2s_out *i2s_out) void i2s_out_dma_free(struct i2s_out *i2s_out) { free(i2s_out->dma_end_buf); - free(i2s_out->dma_out_buf); - free(i2s_out->dma_out_desc); + free(i2s_out->dma_data_buf); + free(i2s_out->dma_data_desc); free(i2s_out->dma_repeat_desc); free(i2s_out->dma_end_desc); } diff --git a/components/i2s_out/esp32/intr_iram.c b/components/i2s_out/esp32/intr_iram.c index 67371792..ad4b4a6a 100644 --- a/components/i2s_out/esp32/intr_iram.c +++ b/components/i2s_out/esp32/intr_iram.c @@ -19,7 +19,7 @@ static void i2s_out_intr_dma_eof_desc(struct i2s_out *i2s_out, struct dma_desc * i2s_dma_desc_reset(desc); } } else { - for (struct dma_desc *desc = eof_desc; desc >= i2s_out->dma_out_desc; desc--) { + for (struct dma_desc *desc = eof_desc; desc >= i2s_out->dma_data_desc; desc--) { if (desc != eof_desc) { LOG_ISR_WARN("miss desc=%p owner=%u len=%u", desc, desc->owner, desc->len); } @@ -72,19 +72,19 @@ static void i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *pxHigh struct dma_desc *eof_desc = (struct dma_desc *) eof_addr; // only handle normal out_desc, not repeat_desc or end_desc - if (eof_desc >= i2s_out->dma_out_desc && eof_desc < i2s_out->dma_out_desc + i2s_out->dma_out_count) { + if (eof_desc >= i2s_out->dma_data_desc && eof_desc < i2s_out->dma_data_desc + i2s_out->dma_data_count) { LOG_ISR_DEBUG("eof desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); i2s_out_intr_dma_eof_desc(i2s_out, eof_desc, pxHigherPriorityTaskWoken); - } else if (i2s_out->dma_repeat_desc && eof_desc >= i2s_out->dma_repeat_desc && eof_desc < i2s_out->dma_repeat_desc + i2s_out->dma_out_count * i2s_out->dma_repeat_count) { + } else if (i2s_out->dma_repeat_desc && eof_desc >= i2s_out->dma_repeat_desc && eof_desc < i2s_out->dma_repeat_desc + i2s_out->dma_data_count * i2s_out->dma_repeat_count) { LOG_ISR_DEBUG("ignore repeat desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); } else if (eof_desc == i2s_out->dma_end_desc) { // we may miss some EOF ISR for intermediate DMA descriptors, unblock i2s_out_dma_wait() LOG_ISR_WARN("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); - eof_desc = i2s_out->dma_out_desc + i2s_out->dma_out_count - 1; + eof_desc = i2s_out->dma_data_desc + i2s_out->dma_data_count - 1; i2s_out_intr_dma_eof_desc(i2s_out, eof_desc, pxHigherPriorityTaskWoken); i2s_out_intr_dma_done(i2s_out, pxHigherPriorityTaskWoken); diff --git a/components/i2s_out/i2s_out.h b/components/i2s_out/i2s_out.h index 6b9e499f..d431802f 100644 --- a/components/i2s_out/i2s_out.h +++ b/components/i2s_out/i2s_out.h @@ -52,17 +52,17 @@ struct i2s_out { #endif /* dma */ - uint8_t *dma_out_buf, *dma_end_buf; - struct dma_desc *dma_out_desc; + uint8_t *dma_data_buf, *dma_end_buf; + struct dma_desc *dma_data_desc; struct dma_desc *dma_repeat_desc; struct dma_desc *dma_end_desc; - unsigned dma_out_count, dma_repeat_count; + unsigned dma_data_count, dma_repeat_count; - // pointer to software-owned dma_out_desc used for write() + // pointer to software-owned dma_data_desc used for write() struct dma_desc *dma_write_desc; - // pointer to previous hardware-owned dma_out_desc, now available for write() - updated by ISR + // pointer to previous hardware-owned dma_data_desc, now available for write() - updated by ISR struct dma_desc *dma_eof_desc; bool dma_start; // initialized by i2s_out_dma_setup(), set by i2s_out_dma_start(), cleared by i2s_out_dma_stop() From eef3748696aae63dfdf17e70944b12a8871bfb23 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Fri, 5 Jun 2026 13:10:45 +0300 Subject: [PATCH 58/66] i2s_out: simplified dma_out_desc using next --- components/i2s_out/esp32/dma.c | 28 +++++++-------- components/i2s_out/esp32/intr_iram.c | 53 +++++++++++----------------- components/i2s_out/i2s_out.h | 10 +++--- 3 files changed, 39 insertions(+), 52 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 55fd949a..5513e010 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -196,8 +196,8 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt // reset eof/write state i2s_out->dma_start = false; i2s_out->dma_write_desc = i2s_out->dma_data_desc; - i2s_out->dma_eof_desc = NULL; - i2s_out->dma_eof_task = NULL; + i2s_out->dma_out_desc = NULL; + i2s_out->dma_out_task = NULL; i2s_out->dma_done = false; i2s_out->dma_done_task = NULL; @@ -217,7 +217,7 @@ struct dma_desc *i2s_out_dma_wait(struct i2s_out *i2s_out, TickType_t timeout) taskENTER_CRITICAL(&i2s_out->mux); if ((dma_write_owner = i2s_out->dma_write_desc->owner)) { - i2s_out->dma_eof_task = xTaskGetCurrentTaskHandle(); + i2s_out->dma_out_task = xTaskGetCurrentTaskHandle(); } taskEXIT_CRITICAL(&i2s_out->mux); @@ -226,17 +226,17 @@ struct dma_desc *i2s_out_dma_wait(struct i2s_out *i2s_out, TickType_t timeout) LOG_DEBUG("done"); break; - } else if (!xTaskNotifyWait(0, I2S_OUT_TASK_NOTIFY_BIT_DMA_EOF, &bits, timeout)) { - LOG_ERROR("timeout -> bits=%08x, dma_data_desc=%p...%p dma_write_desc=%p dma_eof_desc=%p dma_end_desc=%p dma_done=%d", + } else if (!xTaskNotifyWait(0, I2S_OUT_TASK_NOTIFY_BIT_DMA_OUT, &bits, timeout)) { + LOG_ERROR("timeout bits=%08x, dma_data_desc=%p...%p dma_write_desc=%p dma_out_desc=%p dma_end_desc=%p dma_done=%d", bits, i2s_out->dma_data_desc, i2s_out->dma_data_desc + i2s_out->dma_data_count - 1, i2s_out->dma_write_desc, - i2s_out->dma_eof_desc, + i2s_out->dma_out_desc, i2s_out->dma_end_desc, i2s_out->dma_done ); - i2s_out->dma_eof_task = NULL; + i2s_out->dma_out_task = NULL; return NULL; } else { @@ -508,9 +508,9 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) i2s_out->dma_end_desc->next ); - // reset eof/done state - i2s_out->dma_eof_desc = NULL; - i2s_out->dma_eof_task = NULL; + // initialize eof/done state + i2s_out->dma_out_desc = i2s_out->dma_data_desc; + i2s_out->dma_out_task = NULL; i2s_out->dma_done = false; i2s_out->dma_done_task = NULL; @@ -564,11 +564,11 @@ int i2s_out_dma_flush(struct i2s_out *i2s_out, TickType_t timeout) return 0; } else if (!xTaskNotifyWait(0, I2S_OUT_TASK_NOTIFY_BIT_DMA_DONE, &bits, timeout)) { - LOG_ERROR("timeout -> bits=%08x, dma_data_desc=%p...%p dma_write_desc=%p dma_eof_desc=%p dma_end_desc=%p dma_done=%d", + LOG_ERROR("timeout -> bits=%08x, dma_data_desc=%p...%p dma_write_desc=%p dma_out_desc=%p dma_end_desc=%p dma_done=%d", bits, i2s_out->dma_data_desc, i2s_out->dma_data_desc + i2s_out->dma_data_count - 1, i2s_out->dma_write_desc, - i2s_out->dma_eof_desc, + i2s_out->dma_out_desc, i2s_out->dma_end_desc, i2s_out->dma_done ); @@ -606,8 +606,8 @@ void i2s_out_dma_stop(struct i2s_out *i2s_out) taskEXIT_CRITICAL(&i2s_out->mux); // reset eof state - i2s_out->dma_eof_desc = NULL; - i2s_out->dma_eof_task = NULL; + i2s_out->dma_out_desc = NULL; + i2s_out->dma_out_task = NULL; i2s_out->dma_done = false; i2s_out->dma_done_task = NULL; } diff --git a/components/i2s_out/esp32/intr_iram.c b/components/i2s_out/esp32/intr_iram.c index ad4b4a6a..8bae5b8e 100644 --- a/components/i2s_out/esp32/intr_iram.c +++ b/components/i2s_out/esp32/intr_iram.c @@ -7,34 +7,24 @@ #include // we may miss some EOF ISR for intermediate DMA descriptors, ensure we unblock i2s_out_dma_wait() on owner up to eof_desc -static void i2s_out_intr_dma_eof_desc(struct i2s_out *i2s_out, struct dma_desc *eof_desc, BaseType_t *pxHigherPriorityTaskWoken) +static void i2s_out_intr_dma_out_desc(struct i2s_out *i2s_out, struct dma_desc *eof_desc, BaseType_t *pxHigherPriorityTaskWoken) { - // update dma_eof_desc - if (i2s_out->dma_eof_desc) { - for (struct dma_desc *desc = eof_desc; desc > i2s_out->dma_eof_desc; desc--) { - if (desc != eof_desc) { - LOG_ISR_WARN("miss desc=%p owner=%u len=%u", desc, desc->owner, desc->len); - } - - i2s_dma_desc_reset(desc); - } - } else { - for (struct dma_desc *desc = eof_desc; desc >= i2s_out->dma_data_desc; desc--) { - if (desc != eof_desc) { - LOG_ISR_WARN("miss desc=%p owner=%u len=%u", desc, desc->owner, desc->len); - } + // update dma_out_desc and reset descs for write + for (struct dma_desc *desc = i2s_out->dma_out_desc; desc != eof_desc; desc = desc->next) { + LOG_ISR_WARN("missed desc=%p owner=%u len=%u", desc, desc->owner, desc->len); - i2s_dma_desc_reset(desc); - } + i2s_dma_desc_reset(desc); } - i2s_out->dma_eof_desc = eof_desc; + i2s_dma_desc_reset(eof_desc); + + i2s_out->dma_out_desc = eof_desc->next; // unblock wait() - if (i2s_out->dma_eof_task) { - xTaskNotifyFromISR(i2s_out->dma_eof_task, I2S_OUT_TASK_NOTIFY_BIT_DMA_EOF, eSetBits, pxHigherPriorityTaskWoken); + if (i2s_out->dma_out_task) { + xTaskNotifyFromISR(i2s_out->dma_out_task, I2S_OUT_TASK_NOTIFY_BIT_DMA_OUT, eSetBits, pxHigherPriorityTaskWoken); - i2s_out->dma_eof_task = NULL; + i2s_out->dma_out_task = NULL; } } @@ -67,30 +57,27 @@ static void i2s_intr_out_eof_handler(struct i2s_out *i2s_out, BaseType_t *pxHigh i2s_intr_clear(i2s_out->dev, I2S_OUT_EOF_INT_CLR); i2s_ll_tx_get_eof_des_addr(i2s_out->dev, &eof_addr); - LOG_ISR_DEBUG("desc=%p", eof_addr); - struct dma_desc *eof_desc = (struct dma_desc *) eof_addr; - // only handle normal out_desc, not repeat_desc or end_desc if (eof_desc >= i2s_out->dma_data_desc && eof_desc < i2s_out->dma_data_desc + i2s_out->dma_data_count) { - LOG_ISR_DEBUG("eof desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); + LOG_ISR_DEBUG("data desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); - i2s_out_intr_dma_eof_desc(i2s_out, eof_desc, pxHigherPriorityTaskWoken); + i2s_out_intr_dma_out_desc(i2s_out, eof_desc, pxHigherPriorityTaskWoken); } else if (i2s_out->dma_repeat_desc && eof_desc >= i2s_out->dma_repeat_desc && eof_desc < i2s_out->dma_repeat_desc + i2s_out->dma_data_count * i2s_out->dma_repeat_count) { - LOG_ISR_DEBUG("ignore repeat desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); + LOG_ISR_DEBUG("repeat desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); + + i2s_out_intr_dma_out_desc(i2s_out, eof_desc, pxHigherPriorityTaskWoken); } else if (eof_desc == i2s_out->dma_end_desc) { + LOG_ISR_DEBUG("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_out_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_out_desc); + // we may miss some EOF ISR for intermediate DMA descriptors, unblock i2s_out_dma_wait() - LOG_ISR_WARN("end desc=%p owner=%u len=%u, dma_write_desc=%p dma_eof_desc=%p", eof_desc, eof_desc->owner, eof_desc->len, i2s_out->dma_write_desc, i2s_out->dma_eof_desc); - - eof_desc = i2s_out->dma_data_desc + i2s_out->dma_data_count - 1; - - i2s_out_intr_dma_eof_desc(i2s_out, eof_desc, pxHigherPriorityTaskWoken); + i2s_out_intr_dma_out_desc(i2s_out, eof_desc, pxHigherPriorityTaskWoken); i2s_out_intr_dma_done(i2s_out, pxHigherPriorityTaskWoken); } else { - LOG_ISR_ERROR("ignore desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); + LOG_ISR_ERROR("unknown desc=%p owner=%u len=%u", eof_desc, eof_desc->owner, eof_desc->len); } } diff --git a/components/i2s_out/i2s_out.h b/components/i2s_out/i2s_out.h index d431802f..4b6f3bf1 100644 --- a/components/i2s_out/i2s_out.h +++ b/components/i2s_out/i2s_out.h @@ -13,7 +13,7 @@ struct dma_desc; #if CONFIG_IDF_TARGET_ESP32 - #define I2S_OUT_TASK_NOTIFY_BIT_DMA_EOF (1 << 0) + #define I2S_OUT_TASK_NOTIFY_BIT_DMA_OUT (1 << 0) #define I2S_OUT_TASK_NOTIFY_BIT_DMA_DONE (1 << 1) #define I2S_OUT_TASK_NOTIFY_BIT_I2S_DONE (1 << 2) #endif @@ -59,17 +59,17 @@ struct i2s_out { unsigned dma_data_count, dma_repeat_count; - // pointer to software-owned dma_data_desc used for write() + // software-owned dma_data_desc used for write() struct dma_desc *dma_write_desc; - // pointer to previous hardware-owned dma_data_desc, now available for write() - updated by ISR - struct dma_desc *dma_eof_desc; + // current hardware-owned dma desc (data, repeat, end, NULL) - updated by ISR + struct dma_desc *dma_out_desc; bool dma_start; // initialized by i2s_out_dma_setup(), set by i2s_out_dma_start(), cleared by i2s_out_dma_stop() bool dma_done; bool i2s_done; - TaskHandle_t dma_eof_task; // task waiting for dma_eof_desc to update + TaskHandle_t dma_out_task; // task waiting for dma_out_desc to update TaskHandle_t dma_done_task; // task waiting for dma_done to be set TaskHandle_t i2s_done_task; // task waiting for i2s_done to be set }; From 9f2183d90ce304b566774f981f16a08d38ec7fc5 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Fri, 5 Jun 2026 13:11:14 +0300 Subject: [PATCH 59/66] i2s_out: dma wait/flush debug log --- components/i2s_out/esp32/dma.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 5513e010..1a750914 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -223,8 +223,6 @@ struct dma_desc *i2s_out_dma_wait(struct i2s_out *i2s_out, TickType_t timeout) taskEXIT_CRITICAL(&i2s_out->mux); if (!dma_write_owner) { - LOG_DEBUG("done"); - break; } else if (!xTaskNotifyWait(0, I2S_OUT_TASK_NOTIFY_BIT_DMA_OUT, &bits, timeout)) { LOG_ERROR("timeout bits=%08x, dma_data_desc=%p...%p dma_write_desc=%p dma_out_desc=%p dma_end_desc=%p dma_done=%d", @@ -240,7 +238,14 @@ struct dma_desc *i2s_out_dma_wait(struct i2s_out *i2s_out, TickType_t timeout) return NULL; } else { - LOG_DEBUG("wait -> done"); + LOG_DEBUG("wait bits=%08x, dma_data_desc=%p...%p dma_write_desc=%p dma_out_desc=%p dma_end_desc=%p dma_done=%d", + bits, + i2s_out->dma_data_desc, i2s_out->dma_data_desc + i2s_out->dma_data_count - 1, + i2s_out->dma_write_desc, + i2s_out->dma_out_desc, + i2s_out->dma_end_desc, + i2s_out->dma_done + ); break; } @@ -560,11 +565,18 @@ int i2s_out_dma_flush(struct i2s_out *i2s_out, TickType_t timeout) taskEXIT_CRITICAL(&i2s_out->mux); if (dma_done) { - LOG_DEBUG("done"); + LOG_DEBUG("done bits=%08x, dma_data_desc=%p...%p dma_write_desc=%p dma_out_desc=%p dma_end_desc=%p dma_done=%d", + bits, + i2s_out->dma_data_desc, i2s_out->dma_data_desc + i2s_out->dma_data_count - 1, + i2s_out->dma_write_desc, + i2s_out->dma_out_desc, + i2s_out->dma_end_desc, + i2s_out->dma_done + ); return 0; } else if (!xTaskNotifyWait(0, I2S_OUT_TASK_NOTIFY_BIT_DMA_DONE, &bits, timeout)) { - LOG_ERROR("timeout -> bits=%08x, dma_data_desc=%p...%p dma_write_desc=%p dma_out_desc=%p dma_end_desc=%p dma_done=%d", + LOG_ERROR("timeout bits=%08x, dma_data_desc=%p...%p dma_write_desc=%p dma_out_desc=%p dma_end_desc=%p dma_done=%d", bits, i2s_out->dma_data_desc, i2s_out->dma_data_desc + i2s_out->dma_data_count - 1, i2s_out->dma_write_desc, @@ -577,7 +589,14 @@ int i2s_out_dma_flush(struct i2s_out *i2s_out, TickType_t timeout) return -1; } else { - LOG_DEBUG("wait -> done"); + LOG_DEBUG("wait bits=%08x, dma_data_desc=%p...%p dma_write_desc=%p dma_out_desc=%p dma_end_desc=%p dma_done=%d", + bits, + i2s_out->dma_data_desc, i2s_out->dma_data_desc + i2s_out->dma_data_count - 1, + i2s_out->dma_write_desc, + i2s_out->dma_out_desc, + i2s_out->dma_end_desc, + i2s_out->dma_done + ); return 0; } From 0632d9b550f90874fd1568aba251f03e81e3e2c9 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Fri, 5 Jun 2026 13:26:53 +0300 Subject: [PATCH 60/66] i2s_out: fix dma end len --- components/i2s_out/esp32/dma.c | 19 ++++++++++++------- components/i2s_out/i2s_out.h | 1 + 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 1a750914..3a6c7767 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -131,19 +131,22 @@ int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align, unsigne } /* Prepare end desc + buffer */ -void init_dma_end(struct dma_desc *eof_desc, uint32_t value, unsigned count) +size_t init_dma_end(struct dma_desc *end_desc, uint32_t value, unsigned count) { - uint32_t *ptr = (uint32_t *) eof_desc->buf; + uint32_t *ptr = (uint32_t *) end_desc->buf; + size_t len = count * sizeof(value); for (unsigned i = 0; i < count; i++) { ptr[i] = value; } - eof_desc->len = count * sizeof(value); - eof_desc->next = NULL; + end_desc->len = len; + end_desc->next = NULL; + + return len; } -void reinit_dma_desc(struct dma_desc *head, unsigned count, struct dma_desc *next) +void reset_dma_desc(struct dma_desc *head, unsigned count, struct dma_desc *next) { struct dma_desc **nextp = NULL; @@ -174,9 +177,10 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt LOG_ERROR("eof_count=%u is too large for end buf size=%u", options->eof_count, DMA_END_BUF_SIZE); return -1; } + // reinit out desc - init_dma_end(i2s_out->dma_end_desc, options->eof_value, options->eof_count); - reinit_dma_desc(i2s_out->dma_data_desc, i2s_out->dma_data_count, i2s_out->dma_end_desc); + reset_dma_desc(i2s_out->dma_data_desc, i2s_out->dma_data_count, i2s_out->dma_end_desc); + i2s_out->dma_end_len = init_dma_end(i2s_out->dma_end_desc, options->eof_value, options->eof_count); taskENTER_CRITICAL(&i2s_out->mux); @@ -473,6 +477,7 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) i2s_out->dma_write_desc->owner = 1; } + i2s_out->dma_end_desc->len = i2s_out->dma_end_len; i2s_out->dma_end_desc->eof = 1; i2s_out->dma_end_desc->owner = 1; i2s_out->dma_end_desc->next = NULL; diff --git a/components/i2s_out/i2s_out.h b/components/i2s_out/i2s_out.h index 4b6f3bf1..a0823f7c 100644 --- a/components/i2s_out/i2s_out.h +++ b/components/i2s_out/i2s_out.h @@ -58,6 +58,7 @@ struct i2s_out { struct dma_desc *dma_end_desc; unsigned dma_data_count, dma_repeat_count; + size_t dma_end_len; // software-owned dma_data_desc used for write() struct dma_desc *dma_write_desc; From 41d951a8d794ffea2ead2c09ea71edb6a9c544fd Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Fri, 5 Jun 2026 14:11:39 +0300 Subject: [PATCH 61/66] i2s_out: fix NULL deref on multiple EOF ISRs --- components/i2s_out/esp32/intr_iram.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/i2s_out/esp32/intr_iram.c b/components/i2s_out/esp32/intr_iram.c index 8bae5b8e..72488c9b 100644 --- a/components/i2s_out/esp32/intr_iram.c +++ b/components/i2s_out/esp32/intr_iram.c @@ -1,4 +1,4 @@ -#define ERROR +#define ERROR #include "../i2s_out.h" #include "dma.h" @@ -10,7 +10,7 @@ static void i2s_out_intr_dma_out_desc(struct i2s_out *i2s_out, struct dma_desc *eof_desc, BaseType_t *pxHigherPriorityTaskWoken) { // update dma_out_desc and reset descs for write - for (struct dma_desc *desc = i2s_out->dma_out_desc; desc != eof_desc; desc = desc->next) { + for (struct dma_desc *desc = i2s_out->dma_out_desc; (desc != NULL) && (desc != eof_desc); desc = desc->next) { LOG_ISR_WARN("missed desc=%p owner=%u len=%u", desc, desc->owner, desc->len); i2s_dma_desc_reset(desc); From 0912628b184fe75bf19ef0b009bf65642c8ee56e Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Fri, 5 Jun 2026 14:12:11 +0300 Subject: [PATCH 62/66] leds: hard reset i2s on tx errors without start/flush or timeout --- components/i2s_out/i2s_out.c | 34 ++++++++++++++ components/i2s_out/include/i2s_out.h | 11 +++++ components/leds/include/leds.h | 17 +++++++ components/leds/interface.c | 64 ++++++++++++++++++++++++++ components/leds/interfaces/i2s/setup.c | 18 ++++++++ main/leds.c | 8 ++-- 6 files changed, 149 insertions(+), 3 deletions(-) diff --git a/components/i2s_out/i2s_out.c b/components/i2s_out/i2s_out.c index 992cbcdb..91047375 100644 --- a/components/i2s_out/i2s_out.c +++ b/components/i2s_out/i2s_out.c @@ -524,6 +524,40 @@ int i2s_out_close(struct i2s_out *i2s_out, TickType_t timeout) return err; } +int i2s_out_reset(struct i2s_out *i2s_out) +{ + int err = 0; + + if (!xSemaphoreTakeRecursive(i2s_out->mutex, 0)) { + LOG_ERROR("xSemaphoreTakeRecursive"); + return -1; + } + + if (!i2s_out->setup) { + LOG_WARN("setup"); + err = 1; + goto error; + } + + i2s_out_dma_stop(i2s_out); + i2s_out_i2s_stop(i2s_out); + i2s_out_pin_teardown(i2s_out); + + i2s_out->setup = false; + + if (!xSemaphoreGiveRecursive(i2s_out->mutex)) { + LOG_WARN("xSemaphoreGiveRecursive"); + } + +error: + // from open() + if (!xSemaphoreGiveRecursive(i2s_out->mutex)) { + LOG_WARN("xSemaphoreGiveRecursive"); + } + + return err; +} + int i2s_out_teardown(struct i2s_out *i2s_out) { int err = 0; diff --git a/components/i2s_out/include/i2s_out.h b/components/i2s_out/include/i2s_out.h index 63b0c5ca..6a3d02ae 100644 --- a/components/i2s_out/include/i2s_out.h +++ b/components/i2s_out/include/i2s_out.h @@ -251,6 +251,17 @@ int i2s_out_flush(struct i2s_out *i2s_out, TickType_t timeout); */ int i2s_out_close(struct i2s_out *i2s_out, TickType_t timeout); +/** + * Reset I2S output, and release the lock acquired using `i2s_out_open()`. + * + * Tears down the data_gpio pin. + * + * Can only fail if the interface is locked by a different task. + * + * Returns >0 if the i2s_out is not setup(), <0 on errors. + */ +int i2s_out_reset(struct i2s_out *i2s_out); + /** * Tear down all state setup by i2s_out_open(), including state typically shared across multiple open -> close calls. */ diff --git a/components/leds/include/leds.h b/components/leds/include/leds.h index 74b280e4..72bf06b5 100644 --- a/components/leds/include/leds.h +++ b/components/leds/include/leds.h @@ -452,6 +452,11 @@ unsigned leds_count_total_power(struct leds *leds); */ bool leds_is_active(struct leds *leds); +/* + * Check for setup() state. + */ +bool leds_is_interface_setup(struct leds *leds); + /* * Setup persistent LEDs interface. * @@ -470,5 +475,17 @@ int leds_tx(struct leds *leds); /* * Close persistent LEDs interface. + * + * Waits for any in-progress tx to complete, leaving the interface in a defined state. */ int leds_interface_close(struct leds *leds); + +/* + * Reset persistent LEDs interface. + * + * Used to recover from errors, where the interface is in an undefined state. + * + * Does not timeout, can only fail if the i2s_out is locked by a different task. + */ +int leds_interface_reset(struct leds *leds); + diff --git a/components/leds/interface.c b/components/leds/interface.c index 6f5f5a96..3173208a 100644 --- a/components/leds/interface.c +++ b/components/leds/interface.c @@ -69,6 +69,38 @@ int leds_interface_init(union leds_interface_state *interface, const struct leds return 0; } +bool leds_is_interface_setup(struct leds *leds) +{ + switch (leds->options.interface) { + case LEDS_INTERFACE_NONE: + return 0; + + #if CONFIG_LEDS_SPI_ENABLED + case LEDS_INTERFACE_SPI: + return 0; + #endif + + #if CONFIG_LEDS_UART_ENABLED + case LEDS_INTERFACE_UART: + return 0; + #endif + + #if CONFIG_LEDS_I2S_ENABLED + # if LEDS_I2S_INTERFACE_COUNT > 0 + case LEDS_INTERFACE_I2S0: + # endif + # if LEDS_I2S_INTERFACE_COUNT > 1 + case LEDS_INTERFACE_I2S1: + # endif + return leds->interface.i2s.i2s_out_setup; + #endif + + default: + LOG_ERROR("unsupported interface=%#x", leds->options.interface); + return -1; + } +} + int leds_interface_setup(struct leds *leds) { switch (leds->options.interface) { @@ -164,3 +196,35 @@ int leds_interface_close(struct leds *leds) return -1; } } + +int leds_interface_reset(struct leds *leds) +{ + switch (leds->options.interface) { + case LEDS_INTERFACE_NONE: + return 0; + + #if CONFIG_LEDS_SPI_ENABLED + case LEDS_INTERFACE_SPI: + return 0; + #endif + + #if CONFIG_LEDS_UART_ENABLED + case LEDS_INTERFACE_UART: + return 0; + #endif + + #if CONFIG_LEDS_I2S_ENABLED + # if LEDS_I2S_INTERFACE_COUNT > 0 + case LEDS_INTERFACE_I2S0: + # endif + # if LEDS_I2S_INTERFACE_COUNT > 1 + case LEDS_INTERFACE_I2S1: + # endif + return leds_interface_i2s_reset(&leds->interface.i2s); + #endif + + default: + LOG_ERROR("unsupported interface=%#x", leds->options.interface); + return -1; + } +} diff --git a/components/leds/interfaces/i2s/setup.c b/components/leds/interfaces/i2s/setup.c index 270f226d..6e6054f9 100644 --- a/components/leds/interfaces/i2s/setup.c +++ b/components/leds/interfaces/i2s/setup.c @@ -179,3 +179,21 @@ int leds_interface_i2s_close(struct leds_interface_i2s *interface) return err; } + +int leds_interface_i2s_reset(struct leds_interface_i2s *interface) +{ + int err = 0; + +#if CONFIG_LEDS_GPIO_ENABLED + leds_gpio_close(&interface->gpio); +#endif + + if ((err = i2s_out_reset(interface->i2s_out))) { + LOG_ERROR("i2s_out_reset"); + return err; + } + + interface->i2s_out_setup = false; + + return err; +} diff --git a/main/leds.c b/main/leds.c index dd4d0b9f..07547bb8 100644 --- a/main/leds.c +++ b/main/leds.c @@ -181,14 +181,16 @@ int reset_leds(struct leds_state *state) return -1; } - if (!state->config->interface_setup) { + if (!leds_is_interface_setup(state->leds)) { return 0; } LOG_WARN("Reset LEDS interface"); - if ((err = leds_interface_close(state->leds))) { - LOG_WARN("leds_interface_close"); + if ((err = leds_interface_reset(state->leds))) { + // crash and restart + LOG_FATAL("leds_interface_reset"); + return err; } if ((err = leds_interface_setup(state->leds))) { From 2af5561c7ce6ae27511c165c2ad52a2ba7ca4a58 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Fri, 5 Jun 2026 14:31:04 +0300 Subject: [PATCH 63/66] i2s_out: move repeat to setup() to work in async mode, commit all descs in start --- components/i2s_out/esp32/dma.c | 122 +++++++++++-------------- components/i2s_out/i2s_out.c | 27 ------ components/i2s_out/i2s_out.h | 1 - components/i2s_out/include/i2s_out.h | 14 +-- components/leds/interfaces/i2s/setup.c | 4 + components/leds/interfaces/i2s/tx.c | 7 -- 6 files changed, 58 insertions(+), 117 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 3a6c7767..da0911b6 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -131,7 +131,7 @@ int i2s_out_dma_init(struct i2s_out *i2s_out, size_t size, size_t align, unsigne } /* Prepare end desc + buffer */ -size_t init_dma_end(struct dma_desc *end_desc, uint32_t value, unsigned count) +static size_t init_dma_end(struct dma_desc *end_desc, uint32_t value, unsigned count) { uint32_t *ptr = (uint32_t *) end_desc->buf; size_t len = count * sizeof(value); @@ -146,7 +146,7 @@ size_t init_dma_end(struct dma_desc *end_desc, uint32_t value, unsigned count) return len; } -void reset_dma_desc(struct dma_desc *head, unsigned count, struct dma_desc *next) +static void reset_dma_desc(struct dma_desc *head, unsigned count, struct dma_desc *next) { struct dma_desc **nextp = NULL; @@ -169,6 +169,33 @@ void reset_dma_desc(struct dma_desc *head, unsigned count, struct dma_desc *next } } +static void init_dma_repeat(struct i2s_out *i2s_out, unsigned count, struct dma_desc *next) +{ + struct dma_desc **nextp = NULL; + + LOG_DEBUG("count=%u", count); + + for (unsigned i = 0; i < count; i++) { + for (unsigned j = 0; j < i2s_out->dma_data_count; j++) { + struct dma_desc *s = &i2s_out->dma_data_desc[j]; + struct dma_desc *d = &i2s_out->dma_repeat_desc[i * i2s_out->dma_data_count + j]; + + if (nextp) { + *nextp = d; + } + + d->len = s->len; + d->owner = s->owner; + + nextp = &d->next; + } + } + + if (nextp) { + *nextp = next; + } +} + int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *options) { LOG_DEBUG("..."); @@ -179,7 +206,8 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt } // reinit out desc - reset_dma_desc(i2s_out->dma_data_desc, i2s_out->dma_data_count, i2s_out->dma_end_desc); + reset_dma_desc(i2s_out->dma_data_desc, i2s_out->dma_data_count, options->repeat_data_count ? i2s_out->dma_repeat_desc : i2s_out->dma_end_desc); + init_dma_repeat(i2s_out, options->repeat_data_count, i2s_out->dma_end_desc); i2s_out->dma_end_len = init_dma_end(i2s_out->dma_end_desc, options->eof_value, options->eof_count); taskENTER_CRITICAL(&i2s_out->mux); @@ -381,59 +409,6 @@ int i2s_out_dma_write(struct i2s_out *i2s_out, const void *data, size_t size, Ti return len; } -int i2s_out_dma_repeat(struct i2s_out *i2s_out, unsigned count) -{ - struct dma_desc **nextp = &i2s_out->dma_write_desc->next; - - if (i2s_out->dma_start) { - LOG_ERROR("dma_start=%u", i2s_out->dma_start); - return -1; - } - // commit - if (!i2s_out->dma_write_desc->owner) { - LOG_DEBUG("commit desc=%p: owner=%u eof=%u buf=%p len=%u size=%u next=%p", - i2s_out->dma_write_desc, - i2s_out->dma_write_desc->owner, - i2s_out->dma_write_desc->eof, - i2s_out->dma_write_desc->buf, - i2s_out->dma_write_desc->len, - i2s_out->dma_write_desc->size, - i2s_out->dma_write_desc->next - ); - - i2s_out->dma_write_desc->owner = 1; - } - - LOG_DEBUG("count=%u", count); - - for (unsigned i = 0; i < count; i++) { - for (unsigned j = 0; j < i2s_out->dma_data_count; j++) { - struct dma_desc *s = &i2s_out->dma_data_desc[j]; - struct dma_desc *d = &i2s_out->dma_repeat_desc[i * i2s_out->dma_data_count + j]; - - if (!s->owner) { - break; - } - - if (nextp) { - *nextp = d; - } - - d->len = s->len; - d->owner = s->owner; - - nextp = &d->next; - } - } - - if (nextp) { - // eof - *nextp = i2s_out->dma_end_desc; - } - - return 0; -} - int i2s_out_dma_pending(struct i2s_out *i2s_out) { // XXX: this will also be true immediately after dma_start()? @@ -462,26 +437,31 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) return -1; } - // commit if not repeat() - if (!i2s_out->dma_write_desc->owner) { + // desc len is reset by out isr, restore from setup() + i2s_out->dma_end_desc->len = i2s_out->dma_end_len; + + assert(i2s_out->dma_end_desc->eof); + assert(i2s_out->dma_end_desc->next == NULL); + + // commit all descs, including repeat and end + for (struct dma_desc *desc = i2s_out->dma_write_desc; desc; desc = desc->next) { + if (desc->owner) { + continue; + } + LOG_DEBUG("commit desc=%p: owner=%u eof=%u buf=%p len=%u size=%u next=%p", - i2s_out->dma_write_desc, - i2s_out->dma_write_desc->owner, - i2s_out->dma_write_desc->eof, - i2s_out->dma_write_desc->buf, - i2s_out->dma_write_desc->len, - i2s_out->dma_write_desc->size, - i2s_out->dma_write_desc->next + desc, + desc->owner, + desc->eof, + desc->buf, + desc->len, + desc->size, + desc->next ); - i2s_out->dma_write_desc->owner = 1; + desc->owner = 1; } - i2s_out->dma_end_desc->len = i2s_out->dma_end_len; - i2s_out->dma_end_desc->eof = 1; - i2s_out->dma_end_desc->owner = 1; - i2s_out->dma_end_desc->next = NULL; - for (unsigned i = 0; i < i2s_out->dma_data_count; i++) { LOG_DEBUG("dma_data_desc[%u]=%p: owner=%d eof=%d len=%u size=%u buf=%p next=%p", i, &i2s_out->dma_data_desc[i], diff --git a/components/i2s_out/i2s_out.c b/components/i2s_out/i2s_out.c index 91047375..8069eb87 100644 --- a/components/i2s_out/i2s_out.c +++ b/components/i2s_out/i2s_out.c @@ -355,33 +355,6 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t } #endif -int i2s_out_repeat(struct i2s_out *i2s_out, unsigned count) -{ - int err = 0; - - if (!xSemaphoreTakeRecursive(i2s_out->mutex, portMAX_DELAY)) { - LOG_ERROR("xSemaphoreTakeRecursive"); - return -1; - } - - if (!i2s_out->setup) { - LOG_ERROR("setup"); - return -1; - } - - if ((err = i2s_out_dma_repeat(i2s_out, count))) { - LOG_ERROR("i2s_out_dma_repeat"); - goto error; - } - - if (!xSemaphoreGiveRecursive(i2s_out->mutex)) { - LOG_WARN("xSemaphoreGiveRecursive"); - } - -error: - return err; -} - int i2s_out_wait(struct i2s_out *i2s_out, TickType_t timeout) { int err = 0; diff --git a/components/i2s_out/i2s_out.h b/components/i2s_out/i2s_out.h index a0823f7c..ba6fd6f6 100644 --- a/components/i2s_out/i2s_out.h +++ b/components/i2s_out/i2s_out.h @@ -81,7 +81,6 @@ int i2s_out_dma_setup(struct i2s_out *i2s_out, const struct i2s_out_options *opt size_t i2s_out_dma_buffer(struct i2s_out *i2s_out, void **ptr, unsigned count, size_t size, TickType_t timeout); void i2s_out_dma_commit(struct i2s_out *i2s_out, unsigned count, size_t size); int i2s_out_dma_write(struct i2s_out *i2s_out, const void *data, size_t size, TickType_t timeout); -int i2s_out_dma_repeat(struct i2s_out *i2s_out, unsigned count); int i2s_out_dma_running(struct i2s_out *i2s_out); int i2s_out_dma_pending(struct i2s_out *i2s_out); int i2s_out_dma_start(struct i2s_out *i2s_out); diff --git a/components/i2s_out/include/i2s_out.h b/components/i2s_out/include/i2s_out.h index 6a3d02ae..bc22add3 100644 --- a/components/i2s_out/include/i2s_out.h +++ b/components/i2s_out/include/i2s_out.h @@ -126,6 +126,9 @@ struct i2s_out_options { // number of parallel data bits in use, 0 -> all unsigned parallel_data_bits; #endif + + // number of times to repeat data, 0 -> off + unsigned repeat_data_count; }; /** @@ -219,17 +222,6 @@ int i2s_out_write_serial32(struct i2s_out *i2s_out, const uint32_t *data, size_t int i2s_out_write_parallel8x32(struct i2s_out *i2s_out, uint32_t *data, unsigned width, TickType_t timeout); #endif -/** - * Setup DMA to repeat all written buffers given count of times. - * - * Can only be called between open() -> write() and flush() -> close() in sync mode. - * Cannot be called after start(), and cannot call write() after. - * Remains in effect until next close() and open(). - * - * Returns <0 on error, 0 on success. - */ -int i2s_out_repeat(struct i2s_out *i2s_out, unsigned count); - /** * Start I2S output, do not wait for TX. * diff --git a/components/leds/interfaces/i2s/setup.c b/components/leds/interfaces/i2s/setup.c index 6e6054f9..ef46c88e 100644 --- a/components/leds/interfaces/i2s/setup.c +++ b/components/leds/interfaces/i2s/setup.c @@ -136,6 +136,10 @@ int leds_interface_i2s_init(struct leds_interface_i2s *interface, const struct l } #endif + if (options->repeat) { + interface->i2s_out_options.repeat_data_count = options->repeat; + } + interface->gpio = options->gpio; interface->stats = stats; diff --git a/components/leds/interfaces/i2s/tx.c b/components/leds/interfaces/i2s/tx.c index e668e2f7..29f1842d 100644 --- a/components/leds/interfaces/i2s/tx.c +++ b/components/leds/interfaces/i2s/tx.c @@ -195,13 +195,6 @@ int leds_interface_i2s_tx(struct leds_interface_i2s *interface, const struct led } } - if (interface->options->repeat) { - if ((err = i2s_out_repeat(interface->i2s_out, interface->options->repeat))) { - LOG_ERROR("i2s_out_repeat"); - goto error; - } - } - if (!setup) { // sync, wait for done before close WITH_STATS_TIMER(&interface->stats->flush) { From 391cdda44174d928e0ebdf62f550d5e9d582e9cd Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Fri, 5 Jun 2026 14:35:38 +0300 Subject: [PATCH 64/66] i2s_out: assert --- components/i2s_out/esp32/dma.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index da0911b6..1b275f22 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -440,9 +440,6 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) // desc len is reset by out isr, restore from setup() i2s_out->dma_end_desc->len = i2s_out->dma_end_len; - assert(i2s_out->dma_end_desc->eof); - assert(i2s_out->dma_end_desc->next == NULL); - // commit all descs, including repeat and end for (struct dma_desc *desc = i2s_out->dma_write_desc; desc; desc = desc->next) { if (desc->owner) { @@ -472,6 +469,9 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) i2s_out->dma_data_desc[i].buf, i2s_out->dma_data_desc[i].next ); + + assert(i2s_out->dma_data_desc[i].eof); + assert(i2s_out->dma_data_desc[i].next); } for (unsigned i = 0; i < i2s_out->dma_repeat_count; i++) { @@ -485,6 +485,9 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) i2s_out->dma_repeat_desc[i * i2s_out->dma_data_count + j].buf, i2s_out->dma_repeat_desc[i * i2s_out->dma_data_count + j].next ); + + assert(i2s_out->dma_repeat_desc[i * i2s_out->dma_data_count + j].eof); + assert(i2s_out->dma_repeat_desc[i * i2s_out->dma_data_count + j].next); } } @@ -498,6 +501,9 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) i2s_out->dma_end_desc->next ); + assert(i2s_out->dma_end_desc->eof); + assert(i2s_out->dma_end_desc->next == NULL); + // initialize eof/done state i2s_out->dma_out_desc = i2s_out->dma_data_desc; i2s_out->dma_out_task = NULL; From e552a9b5622b28b5567bbb8bfb1885cebce06bdd Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Fri, 5 Jun 2026 14:39:52 +0300 Subject: [PATCH 65/66] i2s_out: fix dma repeat desc len on start --- components/i2s_out/esp32/dma.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/components/i2s_out/esp32/dma.c b/components/i2s_out/esp32/dma.c index 1b275f22..944fd2ea 100644 --- a/components/i2s_out/esp32/dma.c +++ b/components/i2s_out/esp32/dma.c @@ -440,6 +440,15 @@ int i2s_out_dma_start(struct i2s_out *i2s_out) // desc len is reset by out isr, restore from setup() i2s_out->dma_end_desc->len = i2s_out->dma_end_len; + for (unsigned i = 0; i < i2s_out->dma_repeat_count; i++) { + for (unsigned j = 0; j < i2s_out->dma_data_count; j++) { + struct dma_desc *s = &i2s_out->dma_data_desc[j]; + struct dma_desc *d = &i2s_out->dma_repeat_desc[i * i2s_out->dma_data_count + j]; + + d->len = s->len; + } + } + // commit all descs, including repeat and end for (struct dma_desc *desc = i2s_out->dma_write_desc; desc; desc = desc->next) { if (desc->owner) { From c197ada84dcf1d2ac5c817fe268006d6fabff846 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Fri, 5 Jun 2026 21:10:00 +0300 Subject: [PATCH 66/66] main: state vs config on leds reset --- main/leds.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/main/leds.c b/main/leds.c index 07547bb8..5ab11e71 100644 --- a/main/leds.c +++ b/main/leds.c @@ -181,22 +181,24 @@ int reset_leds(struct leds_state *state) return -1; } - if (!leds_is_interface_setup(state->leds)) { - return 0; - } - - LOG_WARN("Reset LEDS interface"); + if (leds_is_interface_setup(state->leds)) { + LOG_WARN("Reset LEDS interface"); - if ((err = leds_interface_reset(state->leds))) { - // crash and restart - LOG_FATAL("leds_interface_reset"); - return err; + if ((err = leds_interface_reset(state->leds))) { + // crash and restart + LOG_FATAL("leds_interface_reset"); + return err; + } } - if ((err = leds_interface_setup(state->leds))) { - // crash and restart - LOG_FATAL("leds_interface_setup"); - return err; + if (state->config->interface_setup) { + LOG_WARN("Setup LEDS interface"); + + if ((err = leds_interface_setup(state->leds))) { + // crash and restart + LOG_FATAL("leds_interface_setup"); + return err; + } } return 0;