From a43e38544bf707831350dd53091c7d47eddd41bf Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 10 Sep 2025 09:35:36 +1000 Subject: [PATCH] shared/tinyusb/mp_usbd_cdc: Rewrite USB CDC TX loop. This is related to the previous commit (where due to the new config flag this loop could end up stuck indefinitely if the USB host was disconnected). The previous loop could maybe still get stuck if the low-level USB state and the high-level USB state got out of sync. (Not clearly possible, but hard to say definitely not possible.) To be "belts and braces" careful: - Always run mp_usbd_task() each time around the loop to progress the state. - Always evaluate the timeout if we fail to write anything to the FIFO. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- shared/tinyusb/mp_usbd_cdc.c | 45 +++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/shared/tinyusb/mp_usbd_cdc.c b/shared/tinyusb/mp_usbd_cdc.c index b4151f685c..9b380acefc 100644 --- a/shared/tinyusb/mp_usbd_cdc.c +++ b/shared/tinyusb/mp_usbd_cdc.c @@ -98,32 +98,45 @@ mp_uint_t mp_usbd_cdc_tx_strn(const char *str, mp_uint_t len) { if (!tusb_inited()) { return 0; } + mp_uint_t last_write = mp_hal_ticks_ms(); size_t i = 0; while (i < len) { uint32_t n = len - i; - if (n > CFG_TUD_CDC_EP_BUFSIZE) { - n = CFG_TUD_CDC_EP_BUFSIZE; - } - if (tud_cdc_connected()) { - // If CDC port is connected but the buffer is full, wait for up to USC_CDC_TIMEOUT ms. - mp_uint_t t0 = mp_hal_ticks_ms(); - while (n > tud_cdc_write_available() && (mp_uint_t)(mp_hal_ticks_ms() - t0) < MICROPY_HW_USB_CDC_TX_TIMEOUT) { - mp_event_wait_ms(1); - // Explicitly run the USB stack as the scheduler may be locked (eg we - // are in an interrupt handler), while there is data pending. - mp_usbd_task(); - } + if (tud_cdc_connected()) { // Limit write to available space in tx buffer when connected. + // + // (If not connected then we write everything to the fifo, expecting + // it to overwrite old data so it will have latest data buffered + // when host connects.) n = MIN(n, tud_cdc_write_available()); - if (n == 0) { - break; - } } - // When not connected we always write to usb fifo, ensuring it has latest data. + uint32_t n2 = tud_cdc_write(str + i, n); tud_cdc_write_flush(); i += n2; + + if (i < len) { + if (n2 > 0) { + // reset the timeout each time we successfully write to the FIFO + last_write = mp_hal_ticks_ms(); + } else { + if ((mp_uint_t)(mp_hal_ticks_ms() - last_write) >= MICROPY_HW_USB_CDC_TX_TIMEOUT) { + break; // Timeout + } + + if (tud_cdc_connected()) { + // If we know we're connected then we can wait for host to make + // more space + mp_event_wait_ms(1); + } + } + + // Always explicitly run the USB stack as the scheduler may be + // locked (eg we are in an interrupt handler), while there is data + // or a state change pending. + mp_usbd_task(); + } } return i; }