diff options
author | Angus Gratton <angus@redyak.com.au> | 2025-09-10 09:35:36 +1000 |
---|---|---|
committer | Damien George <damien@micropython.org> | 2025-09-10 12:35:10 +1000 |
commit | a43e38544bf707831350dd53091c7d47eddd41bf (patch) | |
tree | 4a667f92f58bb67d4488a722fac89a48867fd9ca | |
parent | 3ec2e367decd8da968b1b85b65264bbab367bc9c (diff) |
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 <angus@redyak.com.au>
-rw-r--r-- | shared/tinyusb/mp_usbd_cdc.c | 45 |
1 files changed, 29 insertions, 16 deletions
diff --git a/shared/tinyusb/mp_usbd_cdc.c b/shared/tinyusb/mp_usbd_cdc.c index b4151f685..9b380acef 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(); - } // 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; } |