Skip to content

USBX Audio 2.0: SET_CUR request incorrectly rejected for single-frequency devices #221

@NicoCaldo

Description

@NicoCaldo

USBX Audio 2.0: SET_CUR request incorrectly rejected for single-frequency devices

Describe the bug

The USBX Audio 2.0 class implementation in _ux_device_class_audio20_control_process() incorrectly rejects SET_CUR requests from the host when a device is configured to support only a single sampling frequency. Even when the host requests the exact frequency that the device supports (e.g., 48kHz), the device responds with a STALL, causing USB enumeration to fail and preventing audio streaming callbacks from being invoked.

Target device: STM32U5A9J-DK (also affects STM32H7, STM32N6, STM32WBA series)

Eclipse ThreadX version: 6.4.0 (issue present in X-CUBE-AZRTOS-H7 v3.3.0 and current USBX releases 6.2.1)

Toolchain and environment:

  • IAR Embedded Workbench / STM32CubeIDE
  • ThreadX + USBX stack
  • USB Audio 2.0 Device Class

What have you tried to diagnose or workaround this issue:

  1. Used USB protocol analyzer (USBPcap/Wireshark) to capture traffic - confirmed host sends valid SET_CUR request for CS_SAM_FREQ_CONTROL with correct frequency (48000 Hz), but device responds with USBD_STATUS_STALL_PID

  2. Debugged into _ux_device_class_audio20_control_process() and identified the code breaks early in two locations:

    • First break: if (control->ux_device_class_audio20_control_sampling_frequency != 0) break;
    • Second break: if (n_sub <= 1 && res == 0) break;
  3. Discovered workaround: Configure device as "variable frequency" even when supporting only one frequency:

    audio_control[0].ux_device_class_audio20_control_sampling_frequency = 0;  // Must be 0
    audio_control[0].ux_device_class_audio20_control_sampling_frequency_cur = 48000;
    audio_control[0].ux_device_class_audio20_control_sampling_frequency_range = sampling_freq_range;
    
    // Range with dRES = 1 (not 0)
    static UCHAR sampling_freq_range[] = {
        0x01, 0x00,              // wNumSubRanges = 1
        0x80, 0xBB, 0x00, 0x00,  // dMIN = 48000
        0x80, 0xBB, 0x00, 0x00,  // dMAX = 48000
        0x01, 0x00, 0x00, 0x00   // dRES = 1 (critical!)
    };
  4. Community discussion with detailed analysis: https://community.st.com/t5/stm32-mcus-embedded-software/usbx-audio-class-frame-done-callback-not-called/td-p/855262

To Reproduce

Steps to reproduce the behavior:

  1. Create a USBX Audio 2.0 device project using ThreadX
  2. Configure audio control for single frequency support:
    audio_control[0].ux_device_class_audio20_control_cs_id = 0x18;
    audio_control[0].ux_device_class_audio20_control_sampling_frequency = 48000;  // Fixed frequency
    audio_control[0].ux_device_class_audio20_control_sampling_frequency_range = NULL;
  3. Set Clock Source descriptor with bmControls = 0x03 (read/write) or even 0x01 (read-only)
  4. Register frame done callback:
    audio_stream_parameter[0].ux_device_class_audio_stream_parameter_callbacks.ux_device_class_audio_stream_frame_done = USBD_AUDIO_PlaybackStreamFrameDone;
  5. Connect device to PC and attempt to stream audio
  6. Observe:
    • USB enumeration appears successful
    • Host sends SET_CUR request for sampling frequency (Entity ID 24, Control Selector 0x01, frequency 48000 Hz)
    • Device responds with STALL (USBD_STATUS_STALL_PID)
    • _ux_device_stack_transfer_request() returns UX_TRANSFER_BUS_RESET (0x26)
    • Frame done callback is never called

Expected behavior

  1. When host sends SET_CUR request with frequency = 48000 Hz (matching device's supported frequency), device should accept the request and return UX_SUCCESS
  2. Device should only STALL if requested frequency does not match supported frequency
  3. After successful frequency negotiation, ux_device_class_audio_stream_frame_done callback should be invoked for each received audio frame
  4. Audio streaming should work without requiring the "multi-frequency workaround"

Impact

Showstopper - This issue completely prevents USB Audio 2.0 devices with single fixed frequencies from functioning properly. It affects:

  • All audio streaming callbacks (frame_done) are never invoked
  • Cannot implement USB Audio 2.0 devices with fixed sample rates (common use case)
  • Developers must use a non-intuitive workaround that contradicts descriptor semantics
  • ST example code (X-CUBE-AZRTOS) contains this issue and misleads developers

Logs and console output

USB capture showing STALL response:

Frame 14443: Host -> Device (SET_CUR request)
  bmRequestType: 0x21 (Host-to-device, Class, Interface)
  bRequest: 0x01 (CUR)
  wValue: 0x0100 (CS_SAM_FREQ_CONTROL, Channel 0)
  wIndex: 0x1800 (Interface 0, Entity ID 24)
  wLength: 4
  Data: 80 BB 00 00 (48000 Hz in little-endian)

Frame 14444: Device -> Host (STALL response)
  IRP USBD_STATUS: USBD_STATUS_STALL_PID (0xc0000004)
  URB Function: URB_FUNCTION_CONTROL_TRANSFER (0x0008)
  Packet Data Length: 0

Full USB capture and debug logs available if needed.

Additional context

Root cause analysis:

In _ux_device_class_audio20_control_process(), the SET_CUR handler for sampling frequency contains these checks:

case UX_DEVICE_CLASS_AUDIO20_CUR:
    if (request_length != 4)
        break;
    
    /* Check if multiple frequency supported. */
    if (control->ux_device_class_audio20_control_sampling_frequency != 0)
        break;  // ❌ Rejects ALL SET_CUR for fixed frequencies
    
    // ... validation code ...
    
    /* Check if it's fixed single frequency. */
    if (n_sub <= 1 && res == 0)
        break;  // ❌ Also rejects single-frequency ranges

Both conditions cause early break, leading to endpoint STALL at function end.

Proposed fix:

Before rejecting, validate if requested frequency matches supported frequency:

case UX_DEVICE_CLASS_AUDIO20_CUR:
    if (request_length != 4)
        break;
    
    /* Get frequency to set. */
    freq = _ux_utility_long_get(transfer->ux_slave_transfer_request_data_pointer);
    
    /* Check if fixed single frequency. */
    if (control->ux_device_class_audio20_control_sampling_frequency != 0)
    {
        /* Accept if frequency matches */
        if (freq == control->ux_device_class_audio20_control_sampling_frequency)
            return(UX_SUCCESS);
        /* Otherwise reject */
        break;
    }
    
    /* Variable frequency mode - existing validation logic */
    // ...

Related issues:

USB Audio 2.0 spec compliance:

The USB Audio 2.0 specification allows devices with single fixed frequencies to respond to SET_CUR requests. The descriptor's bmControls field should determine if SET_CUR is allowed, not internal implementation details. Current USBX behavior violates this principle.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

Status

Discussion

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions