Skip to content

USB CDC TX stall when ring buffer wraps #5537

Description

@nobonobo

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:

USB IN:
7 bytes
64 bytes

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions