From 28b25ea5d528cd621a7d023b5c3529272c683e15 Mon Sep 17 00:00:00 2001 From: Patryk Kubiak Date: Sun, 11 Oct 2026 11:55:51 +0200 Subject: [PATCH] Config key rx_dma: receive by interrupt where the DMA does not serve the UART The DMA receive added in the previous change cannot work on a platform whose DMA controller is not driven by the UART - Renode's STM32F7 model has no request line from the USART, so a console UART received nothing there. `rx_dma=0` of the UART's section makes the instance receive by interrupt (dmuart_port_set_rx_dma(), called before set_rx_ring(); the default stays DMA). Co-Authored-By: Claude Sonnet 5.5 --- docs/configuration.md | 1 + include/dmuart_port.h | 14 ++++++++++++++ src/dmuart.c | 3 +++ src/port/stm32f4/port.c | 6 ++++++ src/port/stm32f7/port.c | 11 ++++++++++- 5 files changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 62c9fb3..c3092ee 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -18,6 +18,7 @@ All parameters are in the `[dmuart]` section: | `instance` | integer | 1 | UART instance number (1-8) | | `rx_ring_size` | integer | 256 | Capacity of the RX ring buffer, in bytes (0 disables it) | | `rx_ring_wait_mode` | string | "some_data" | RX ring read-wait behavior: "none" (return immediately), "some_data" (wait for at least one byte), "all_data" (wait for the full requested length) | +| `rx_dma` | integer | 1 | Receive by DMA when the port can (STM32F7); 0: by interrupt. For a platform whose DMA does not serve the UART, e.g. an emulator | ### Receiving by DMA (STM32F7) diff --git a/include/dmuart_port.h b/include/dmuart_port.h index 8a78c85..f9319d4 100644 --- a/include/dmuart_port.h +++ b/include/dmuart_port.h @@ -69,4 +69,18 @@ dmod_dmuart_port_api(1.0, int, _remove_interrupt_handler, dmod_dmuart_port_api(1.0, int, _set_rx_ring, ( dmuart_instance_t instance, dm_sw_ring_t ring ) ); +/* --- Receiving by DMA --- */ + +/** + * @brief Allow (or forbid) receiving by DMA for the next set_rx_ring() + * + * A port that can receive by DMA does so by default. Forbidding it makes the + * instance receive by interrupt - for a platform whose DMA does not serve the + * UART (e.g. an emulator). Call it before dmuart_port_set_rx_ring(). + * Ports without DMA receive do nothing. + * + * @return 0 on success, negative on an invalid instance + */ +dmod_dmuart_port_api(1.0, int, _set_rx_dma, ( dmuart_instance_t instance, bool enabled ) ); + #endif // DMUART_PORT_H diff --git a/src/dmuart.c b/src/dmuart.c index 668d54a..e061953 100644 --- a/src/dmuart.c +++ b/src/dmuart.c @@ -25,6 +25,7 @@ struct dmdrvi_context dm_sw_ring_t rx_ring; /**< Software ring buffer for received bytes */ uint32_t rx_ring_size; /**< Capacity of the RX ring buffer (from config) */ dm_sw_ring_flags_t rx_ring_wait_flags; /**< RX ring read-wait behavior (from config) */ + bool rx_dma; /**< Receive by DMA when the port can (from config) */ char *tty_path; /**< Absolute dmdevfs path announced to dmtty (NULL = not announced) */ }; @@ -272,6 +273,7 @@ static int read_config_parameters(dmdrvi_context_t context, dmini_context_t conf context->rx_ring_size = (uint32_t)dmini_get_int(config, section, "rx_ring_size", 256); context->rx_ring_wait_flags = string_to_rx_ring_wait_flags( dmini_get_string(config, section, "rx_ring_wait_mode", "some_data")); + context->rx_dma = dmini_get_int(config, section, "rx_dma", 1) != 0; return check_config_parameters(&context->config); } @@ -466,6 +468,7 @@ dmod_dmdrvi_dif_api_declaration(2.0, dmuart, dmdrvi_context_t, _create, ( dmini_ } else { + (void)dmuart_port_set_rx_dma(context->config.instance, context->rx_dma); dmuart_port_set_rx_ring(context->config.instance, context->rx_ring); } } diff --git a/src/port/stm32f4/port.c b/src/port/stm32f4/port.c index fc9a41d..7886729 100644 --- a/src/port/stm32f4/port.c +++ b/src/port/stm32f4/port.c @@ -525,6 +525,12 @@ dmod_dmuart_port_api_declaration(1.0, int, _set_rx_ring, return 0; } +dmod_dmuart_port_api_declaration(1.0, int, _set_rx_dma, ( dmuart_instance_t instance, bool enabled )) +{ + (void)enabled; /* (receives by interrupt only) */ + return validate_instance(instance); +} + /* ---- ISR handlers ---- */ /* Upper bound on bytes drained from DR per ISR entry. DR itself holds only diff --git a/src/port/stm32f7/port.c b/src/port/stm32f7/port.c index df3651a..de2375a 100644 --- a/src/port/stm32f7/port.c +++ b/src/port/stm32f7/port.c @@ -80,6 +80,7 @@ typedef struct } rx_dma_t; static rx_dma_t rx_dma[STM32F7_UART_MAX_INSTANCES]; +static bool rx_dma_forbidden[STM32F7_UART_MAX_INSTANCES]; /* receive by interrupt (dmuart_port_set_rx_dma()) */ /* NVIC IRQ numbers for STM32F7 UART instances (must match DMOD_IRQ_HANDLER list below) */ static const uint32_t uart_irqn[STM32F7_UART_MAX_INSTANCES] = { @@ -193,6 +194,7 @@ int dmod_init(const Dmod_Config_t *Config) irq_user_ptrs[i] = NULL; rx_rings[i] = NULL; memset(&rx_dma[i], 0, sizeof(rx_dma[i])); + rx_dma_forbidden[i] = false; } return 0; } @@ -771,11 +773,18 @@ dmod_dmuart_port_api_declaration(1.0, int, _set_rx_ring, if (ring == NULL) rx_dma_stop(instance); rx_rings[instance - 1] = ring; - if (ring != NULL) + if (ring != NULL && !rx_dma_forbidden[instance - 1]) (void)rx_dma_start(instance); /* (no stream: by interrupt) */ return 0; } +dmod_dmuart_port_api_declaration(1.0, int, _set_rx_dma, ( dmuart_instance_t instance, bool enabled )) +{ + if (validate_instance(instance) != 0) return -1; + rx_dma_forbidden[instance - 1] = !enabled; + return 0; +} + /* ---- ISR handlers ---- */ /* Upper bound on bytes drained from RDR per ISR entry. RDR itself holds only