Skip to content

usb_audio: mark speaker OUT adaptive so Windows UAC2 can start - #11256

Open
jbirchall-svg wants to merge 2 commits into
adafruit:mainfrom
jbirchall-svg:bugfix/11216-usb-audio-enabling-speaker-fail-to-start
Open

usb_audio: mark speaker OUT adaptive so Windows UAC2 can start#11256
jbirchall-svg wants to merge 2 commits into
adafruit:mainfrom
jbirchall-svg:bugfix/11216-usb-audio-enabling-speaker-fail-to-start

Conversation

@jbirchall-svg

@jbirchall-svg jbirchall-svg commented Aug 26, 2026

Copy link
Copy Markdown

Closes: #11216

Fixes Windows 11 Code 10 ("This device cannot start") when usb_audio.enable(speaker=True) is used.

The speaker (and headset speaker) OUT endpoint was copied from TinyUSB's
TUD_AUDIO20_SPEAKER_*_FB_DESCRIPTOR but the feedback endpoint was
intentionally omitted. The data EP was still marked Asynchronous.
usbaudio2.sys requires an explicit feedback endpoint for async OUT and
does not support implicit feedback, so the driver fails during start
before any audio flows. Linux is more lenient, which is why RP2040
speaker tests looked fine there.

The USBSpeaker sink already adapts: it drains whatever the host sends
and underruns/overruns in the ring. Advertise that honestly as Adaptive
instead of Asynchronous. Microphone IN stays Asynchronous (valid
without a feedback EP).

This is not a full clock-matching implementation. Long-term drift can
still produce drop/insert artifacts; a later change can add a feedback
EP (and TinyUSB CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP) if we need the
board to be the clock master. Explicit feedback is also a poor first
fix on full-speed parts: Windows' UAC2 driver has known FS feedback
format bugs, and TinyUSB's own speaker example emits UAC1 on FS.

Test plan:

  • Windows 11: usb_audio.enable(sample_rate=48000, channel_count=2, microphone=False, speaker=True) — audio device starts, no Code 10, playback reaches I2S
  • Windows 11: usb_audio.enable(speaker=True) (headset; mic still defaults on) — speaker device starts
  • Linux: speaker-only and mic-only still enumerate and stream
  • nRF52840 speaker-only (forced ISO EP 8) still streams

@dhalbert
dhalbert requested a review from FoamyGuy August 26, 2026 20:54
@dhalbert

Copy link
Copy Markdown
Collaborator

@jbirchall-svg The original post looks like LLM output? Have you done the tests listed?

@jbirchall-svg

Copy link
Copy Markdown
Author

@jbirchall-svg The original post looks like LLM output? Have you done the tests listed?

@dhalbert it is LLM output (Cursor AI Grok 4.6 model) and I have only done the Windows 11 Pro tests on a single Adafruit QT Py RP2040 board connected to an Adafruit I2S Amplifier BFF and an 8 Ohm speaker.

Audio streams correctly for me from the PC, through the circuit, and into the speaker after applying the PR and building my fork.

@FoamyGuy FoamyGuy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had an agent confirm no regressions from this change on linux.

I don't have any easy way to test on windows.

TUD_AUDIO20_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \
TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t)((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01), \
TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t)((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ADAPTIVE | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01), \

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is for the microphone endpoint not speaker. Line 269 above here has a comment that starts the beginning of the microphone definitions for headset mode.

I think that line 266 is where this would need to be handled for the speaker side of the headset mode.

I built this branch for metro RP2350 and in headset mode it enumerated as OUT=Async, IN=Adaptive.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Womp. Womp.

You are correct. I'll put up a new commit shortly.

/* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \
TUD_AUDIO20_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \
/* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \
TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t)((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01), \

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the line that would need updated for the speaker half of headset mode.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

usb_audio: Enabling speaker causes Windows Pro 11 USB 2.0 Audio Driver to fail to start the device

3 participants