Zephyr Output of USB UART Serial Leaks Into Input Buffer


I wrote a simple terminal that reads the keyboard input from USB UART device
and echos it back to the same device. It works as follows:

1. It registers an interrupt handler (uart_isr)
2. The handler reads input data and writes it to a buffer
3. Main loop prints the buffer

Unfortunatelly the ISR reads 15 characters from the early Zephyr
boot process standard output "*** Booting Zeph". Subsequent ISR calls
read keystrokes properly. An example application interaction can look as follows:


1. Zephyr boots
2. Main loop prints: `size: 16, buffer: *** Booting Zeph`
3. The user presses 1, 2, 3 on the keyboard
4. Main loop prints: `size: 16, buffer: *** Booting Zeph123`

The application code looks as follows:

#include <zephyr/kernel.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/device.h>
#include <stdint.h> // int32_t

uint32_t buffer_size = 0;
uint8_t buffer[64];

void uart_isr(const device *dev, void *user_data) {
    while (uart_irq_update(dev) == 1 && uart_irq_is_pending(dev) == 1) {
        if (uart_irq_rx_ready(dev) != 1) return;

        uint32_t remaining_space = sizeof(buffer) - buffer_size;
        if (remaining_space > 1) {
            buffer_size += uart_fifo_read(
                dev,
                &buffer[buffer_size],
                remaining_space - 1
            );
        }
    }
}

int main() {
    const device *uart_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
    if (!device_is_ready(uart_dev)) return -ENODEV;
    if (uart_irq_callback_set(uart_dev, uart_isr)) return -ENODEV;

    uart_irq_rx_enable(uart_dev);

    while (1) {
        buffer[buffer_size] = 0;
        printk("size: %i, buffer: %s\n", buffer_size, buffer);
        k_msleep(3000); // give some time to accumulate data in the receive buffer
    }

    return 0;
}



prj.conf:
CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y


The samples/subsys/console/getchar sample exhibits the same behaviour.
Did I do something wrong or is it a Zephyr bug?

Parents
  • I bisected even further. The following commit introduces the bug:

    commit 23a92a6ec889aa817599a56186fb381b0d1a1fa5
    Author: Alexandre Boeglin <[email protected]>
    Date:   Fri Oct 17 16:25:51 2025 +0200

        usb: device_next: cdc_acm: Trigger TX on configuration enable

        When CDC ACM instance gets enabled and there is some data in TX FIFO,
        the TX FIFO work will not be triggered if no UART API is used. And if TX
        FIFO was full before CDC ACM instance get enabled, TX work will never be
        triggered. To fix it TX FIFO should always be drained when CDC ACM gets
        enabled.

        Signed-off-by: Alexandre Boeglin <[email protected]>

  • I have already tried to reproduce what you are observing using the same build target and Zephyr version (checked out at commit hash bcf31c20f0) without success. If you are unable to flash .hex files, please let me know which format you prefer instead. I also had a look at the commit you mentioned and I struggle to see how it could be related to the issue you mentioned. I don't see how this change could result in getchar() returning characters from the boot string.

  • I don't understand how the TX queue leaks into the RX queue as well. I played around with this code and commenting out the changes made in 23a helped with my case. The crucial part was not running the else branch when TX is disabled. The commit broke that.

    The change 23a doesn't even make sense. Why would you even start transmiting when TX is disabled.

Reply
  • I don't understand how the TX queue leaks into the RX queue as well. I played around with this code and commenting out the changes made in 23a helped with my case. The crucial part was not running the else branch when TX is disabled. The commit broke that.

    The change 23a doesn't even make sense. Why would you even start transmiting when TX is disabled.

Children
No Data
Related