From c11717648f36b9aef89008c98e92fc92ffba4c8e Mon Sep 17 00:00:00 2001 From: Mikey Sklar Date: Sun, 11 Oct 2026 21:31:46 -0700 Subject: [PATCH] zephyr-cp: stop dropping USB console output while the port is open The CDC ACM driver drops poll_out bytes when its TX buffer is full and flow control is off, so a long print lost a few hundred characters even with a terminal reading. Turn the driver flow control on for the console while USB is configured, not suspended, and the host holds DTR, and off otherwise. This matches TinyUSB, which only overwrites its TX FIFO when the host is not connected, so a closed, suspended or unplugged port still never blocks output. Co-Authored-By: Claude Opus 5.5 (1M context) --- ports/zephyr-cp/supervisor/usb.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/ports/zephyr-cp/supervisor/usb.c b/ports/zephyr-cp/supervisor/usb.c index f269a38ea59..239d3e02a40 100644 --- a/ports/zephyr-cp/supervisor/usb.c +++ b/ports/zephyr-cp/supervisor/usb.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -217,6 +218,21 @@ int _zephyr_disk_ioctl(struct disk_info *disk, uint8_t cmd, void *buff) { // volatile to avoid the compiler caching stale values. static volatile bool usb_configured = false; +static const struct device *const console_dev = DEVICE_DT_GET(DT_NODELABEL(cdc_acm_console)); +static bool console_dtr = false; +static bool usb_suspended = false; + +// With flow control on, the CDC ACM driver's poll_out waits for room in its +// TX buffer instead of dropping output. Use it for the console only while +// the host is connected, awake and holds DTR, as TinyUSB does. +static void update_console_flow_control(void) { + bool on = usb_configured && !usb_suspended && console_dtr; + struct uart_config config = { + .flow_ctrl = on ? UART_CFG_FLOW_CTRL_RTS_CTS : UART_CFG_FLOW_CTRL_NONE, + }; + uart_configure(console_dev, &config); +} + static void _msg_cb(struct usbd_context *const ctx, const struct usbd_msg *msg) { LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type)); @@ -243,6 +259,20 @@ static void _msg_cb(struct usbd_context *const ctx, const struct usbd_msg *msg) } else if (msg->type == USBD_MSG_RESET) { usb_configured = false; } + + if (msg->type == USBD_MSG_SUSPEND) { + usb_suspended = true; + } else if (msg->type == USBD_MSG_RESUME || msg->type == USBD_MSG_RESET) { + usb_suspended = false; + } + if (msg->type == USBD_MSG_RESET) { + console_dtr = false; + } else if (msg->type == USBD_MSG_CDC_ACM_CONTROL_LINE_STATE && msg->dev == console_dev) { + uint32_t dtr = 0; + uart_line_ctrl_get(console_dev, UART_LINE_CTRL_DTR, &dtr); + console_dtr = dtr != 0; + } + update_console_flow_control(); } void usb_init(void) {