Problem
USB CDC transmission occasionally stalls when sending data larger than the endpoint's maximum packet size.
For example, sending 71 bytes on a 64-byte bulk endpoint should produce:
64-byte IN packet
7-byte IN packet
However, when the TX ring buffer wraps, the current implementation may transmit:
7-byte IN packet
64-byte IN packet
After this sequence, the host CDC stack may stop receiving further data.
Cause
The TX path only uses the first contiguous segment returned by the ring buffer.
For example:
Peek():
data1 = 7 bytes
data2 = 64 bytes
results in:
This generates a short packet before all available TX data has been sent.
Although this is valid at the USB protocol level (it is interpreted as two separate bulk transfers), it can cause interoperability issues with some USB CDC host implementations.
Most USB device stacks (e.g. TinyUSB/Pico SDK based implementations) transmit data by filling endpoint-sized packets first and only generate a short packet for the final remaining data.
Fix
When the ring buffer wraps, combine data1 and data2 to fill a USB packet whenever possible.
Before:
Ring buffer:
[7 bytes][64 bytes]
USB IN:
7 bytes
64 bytes
After:
Ring buffer:
[7 bytes][64 bytes]
USB IN:
64 bytes
7 bytes
After this change, the transmission stall could no longer be reproduced.
Problem
USB CDC transmission occasionally stalls when sending data larger than the endpoint's maximum packet size.
For example, sending 71 bytes on a 64-byte bulk endpoint should produce:
However, when the TX ring buffer wraps, the current implementation may transmit:
After this sequence, the host CDC stack may stop receiving further data.
Cause
The TX path only uses the first contiguous segment returned by the ring buffer.
For example:
results in:
This generates a short packet before all available TX data has been sent.
Although this is valid at the USB protocol level (it is interpreted as two separate bulk transfers), it can cause interoperability issues with some USB CDC host implementations.
Most USB device stacks (e.g. TinyUSB/Pico SDK based implementations) transmit data by filling endpoint-sized packets first and only generate a short packet for the final remaining data.
Fix
When the ring buffer wraps, combine
data1anddata2to fill a USB packet whenever possible.Before:
After:
After this change, the transmission stall could no longer be reproduced.