Skip to content

[USBX Device Audio] _ux_device_class_audio_transmission_start logic prevents Microphone stream start on Isochronous IN #236

@NicoCaldo

Description

@NicoCaldo

Describe the bug
A logic error in the _ux_device_class_audio_transmission_start function prevents the Microphone (Isochronous IN) stream from starting when the buffer is correctly primed. The function checks if (stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_length == 0) and returns UX_BUFFER_OVERFLOW if true. However, because the microphone thread is suspended and hasn't processed its first frame yet, this condition often triggers even when data is waiting, or conversely, blocks the initial resume of the audio thread.

Inverting this logic or bypassing it allows the Host (Windows) to begin sending Isochronous IN tokens, which are otherwise never sent.

Hardware and Software Environment:

  • Target device: STM32U5A9J-DK (using USB OTG HS)
  • Version of Eclipse ThreadX: v6.2.1 or v6.3.0 (USBX Audio 2.0 Class)
  • Toolchain and environment: IAR Embedded Workbench for ARM / STM32CubeIDE
  • Diagnostics tried: - Verified USB Descriptors (Clock Source IDs, Terminal Links, and Endpoint addresses).
  • Monitored USB traffic via Wireshark; confirmed Windows never sends IN tokens for the Mic interface until the library was patched.
  • Traced ux_device_class_audio_write_thread_entry and found it permanently SUSPENDED due to the initialization failure in transmission_start.

To Reproduce
Steps to reproduce the behavior:

  1. Initialize a USBX Audio 2.0 device with both Speaker (OUT) and Microphone (IN) interfaces.
  2. Link the Microphone path to the same Clock Source as the Speaker.
  3. In the ux_device_class_audio_stream_change callback, attempt to prime the Mic buffer using ux_device_class_audio_write_frame_get and ux_device_class_audio_write_frame_commit.
  4. Call ux_device_class_audio_transmission_start.
  5. Observe that the function returns UX_BUFFER_OVERFLOW (0x5D) and the microphone thread never executes.

Expected behavior
The ux_device_class_audio_transmission_start function should successfully resume the audio write thread when a valid stream is configured, allowing the hardware to prepare for the first Host IN token.

Impact
Showstopper. Without the patch/inversion of the logic, it is impossible to implement a USB Audio Microphone or Loopback interface, as the Host fails to recognize the data stream and eventually returns a "Code 10" error in Device Manager.

Logs and console output

  • Wireshark: Shows SET_INTERFACE (Alt 1) for the Mic interface followed by zero Isochronous IN packets.
  • Debugger: stream->ux_device_class_audio_stream_transfer_pos->ux_device_class_audio_frame_length is observed as 0 at the moment of the failing check.

Additional context
The issue seems specifically tied to the initial state of the transfer_pos structure when the stream is transitioned from Alt Setting 0 to Alt Setting 1. Even if the buffer is primed with ux_device_class_audio_write_frame_commit immediately prior to the start call, the pointer/length state within the USBX internal structures does not always satisfy the == 0 check in the way the driver expects.

Workaround: Patching the USBX Library

File: ux_device_class_audio_transmission_start.c

Description: The original logic prevents the transmission thread from resuming if the frame length is 0. However, in many initialization sequences, the length remains 0 until the thread is actually running and the hardware starts its first transfer cycle. By inverting the logic, we allow the thread to resume and the Host to begin polling.

Modified Code:

/* Check if there is frame to send (underflow).  */
/* ORIGINAL: if (stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_length == 0) */

/* PATCHED: Invert logic to allow initial thread resumption */
if (stream -> ux_device_class_audio_stream_transfer_pos -> ux_device_class_audio_frame_length > 0)
{
    return(UX_BUFFER_OVERFLOW);
}

Updated "Additional context" for your report

When using the original == 0 check, a "deadlock" occurs:

  1. The Host sends SET_INTERFACE to enable the Microphone.
  2. ux_device_class_audio_transmission_start() is called.
  3. The check fails (returns UX_BUFFER_OVERFLOW) because the internal transfer_pos hasn't been updated by an active transfer yet.
  4. The Microphone thread remains SUSPENDED.
  5. Because the thread is suspended, no data is ever moved to the FIFO.
  6. The Host (Windows) receives no data for its initial IN tokens, times out, and stops polling, resulting in Error Code 10.

By changing the condition to > 0, the function returns UX_SUCCESS when the length is 0, successfully executing _ux_device_thread_resume(). This allows the USBX audio thread to wake up, interact with the hardware driver, and satisfy the Host's requests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions