Skip to content

usb-cdc overwrites its own tx buffer during write, hence data arrives truncated (IDFGH-7469) #9040

Closed
@moefear85

Description

@moefear85

Environment

  • Module or chip used: esp32-s2
  • IDF version: affects all versions from atleast v4.3.1 to latest master
  • Build System: idf.py
  • Operating System: Linux
  • Using an IDE?: VSCode IDF Extension
  • Power Supply: external 3.3V

Problem Description

When calling tinyusb_cdcacm_write_queue(), then the data sent out is always the last portion of whatever buffer was given, and it depends on the configured cdc tx fifo size in sdkconfig.

Also, any value for rx fifo size less than 64 causes no data to arrive or be detected.

Expected Behavior

That when sending a buffer larger than what tinyusb_cdcacm_write_queue() can handle, that it only write as much as it can, and return that value, so another call can be placed later at proper offsets, until the entire buffer is sent.

One also expects rx fifo to forward any incoming data as long as it fits in its buffer, no matter how small, even if only 1 byte in size (such as a user typing individual characters).

Actual Behavior

The function correctly reports the size it accepted & sent, but instead of taking the first n bytes of the buffer, it takes the last n bytes. This means if the buffer in sdkconfig is chosen large enough, such as 1024 bytes, one might never notice this problem. But at smaller sizes such as 64 bytes, it leads to stream corruption (truncation of beginnings).

if the rx fifo buffer size is any smaller than 64, nothing arrives anymore. If this is not fixable, perhaps a warning somewhere that it has to be atleast 64 bytes would be helpful. It seems if this value is smaller than a single usb packet size, it gets dropped by tinyusb.

Steps to reproduce

  1. Any sample project that tests tinyusb_cdcacm_write_queue(), as long as the size of the buffer passed is larger than the tx FIFO size chosen in sdkconfig (CFG_TUD_CDC_TX_BUFSIZE).

Cause of Problem

The structure "tu_fifo_t" used to hold the outgoing data contains a boolean called "overwritable". The default value seems to be true. Hence when tud_cdc_n_write() is called, and the source buffer is larger than the ringbuffer in "tu_fifo_t", it will wrap around and overwrite itself, so that if the ringbuffer size is m and the passed buffer size is n, then the first (n-m) bytes get lost/overwritten, and only the last n bytes are saved and thus later flushed/written out.

Workaround

I manually set the value of "overwritable" to false before doing any sending, and this solves the problem. No matter how small CFG_TUD_CDC_TX_BUFSIZE (even a value of 1 works), and no matter how large the buffer I pass to tinyusb_cdcacm_write_queue(), it will only queue up to the first n bytes of the passed buffer, correctly returning the size it queued, so that later calls at proper offsets will correctly queue & send out the right offsets.

Note: Even after setting "overwritable" to true, then although I can successfully choose smaller tx fifo sizes without any loss, at some point, I think a size of 1 or 2, it no longer automatically flushes when necessary, hence calling tinyusb_cdcacm_write_queue() will always return 0 unless I manually call tinyusb_cdcacm_write_flush() then try again. But at any tx fifo sizes larger than that, it is not necessary to call tinyusb_cdcacm_write_flush().

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions