Linux Sound subsystem development
 help / color / mirror / Atom feed
* [BUG] snd-usb-audio: Elgato XLR Dock 0fd9:00a6 capture silent when playback starts first
@ 2026-11-07  0:44 James Barnette
  0 siblings, 0 replies; 4+ messages in thread
From: James Barnette @ 2026-11-07  0:44 UTC (permalink / raw)
  To: linux-sound; +Cc: tiwai, perex

[-- Attachment #1: Type: text/plain, Size: 4385 bytes --]

Hello,

I can reproducibly make an Elgato XLR Dock return digital silence from its
microphone by starting playback before capture. Starting capture before
playback works. This has caused repeated microphone failures during normal
desktop use.

I would appreciate guidance on whether this needs an snd-usb-audio device
quirk, and what additional traces or tests would help identify the correct
fix. I have not established whether the underlying issue is device firmware
or a host initialization requirement, and I do not have a tested kernel patch.

Hardware and software
---------------------
Device: Elgato XLR Dock, USB ID 0fd9:00a6
USB bcdDevice: 2.10 (device release descriptor, not independently verified
as the vendor's firmware version)
Connection: full-speed USB through xHCI
Kernel: 7.2.5-3-omarchy, x86_64 (distribution/custom kernel)
ALSA library: 1.2.16.1
PipeWire: 1.6.8
WirePlumber: 0.5.17

I have not yet tested a stock upstream kernel or another kernel version,
so I cannot identify this as a kernel regression.

Reproduction
------------
I temporarily disabled the PipeWire card profile for this device to release
its hardware PCMs. I then ran arecord/aplay directly against hw:4,0. Card
numbers are machine-specific. No PipeWire mixer, WaveSink routing, or
OpenDeck plugin participates in these direct ALSA trials.

Both streams use 48000 Hz and S24_3LE. Playback is two channels of zeros
from /dev/zero; microphone capture is mono. Each capture lasts two seconds.
The microphone was unmuted, with hardware gain 49.5 dB. Hardware settings,
including phantom power, were unchanged between trials.

Failing order:
1. Start aplay -D hw:4,0 -t raw -f S24_3LE -r 48000 -c 2 /dev/zero
2. Wait 300 ms, then start arecord -D hw:4,0 -t raw -f S24_3LE -r
48000 -c 1 -d 2
3. Inspect capture samples, then close both streams.

Working order:
1. Start the same arecord command and wait until it supplies PCM data.
2. Start the same aplay command while capture remains open.
3. Inspect capture samples, then close both streams.

The attached Python reproducer discards audio, prints sample statistics,
validates the exact USB ID, and restores the previous PipeWire card profile
with capture started first. It briefly interrupts this device's audio.
Run: python reproduce-xlrdock-startup.py --card 4

Results
-------
Across two runs, playback-first failed in 4/4 trials and capture-first worked
in 4/4 trials. The second run captured exactly 96000 mono samples per trial:

Playback-first: 0 nonzero samples / 96000
Capture-first: 95700 nonzero samples / 96000
Playback-first: 0 nonzero samples / 96000
Capture-first: 95701 nonzero samples / 96000

USB observations
----------------
In the first run, usbmon showed completed isochronous capture transfers on
endpoint 0x82. For each playback-first trial, all 2012 retained packet data
prefixes were zero. For each capture-first trial, 2006/2012 prefixes contained
nonzero data. I retained statistics and setup-request metadata, not audio or
a complete usbmon recording. This localizes the observed silence to capture
USB buffers, below the application mixer; it does not prove an internal
firmware defect.

The relevant SET_INTERFACE activation order was:
Failing: interface 1 alt 1 (playback), then interface 2 alt 1 (capture)
Working: interface 2 alt 1 (capture), then interface 1 alt 1 (playback)

The descriptors advertise playback endpoint 0x01 with explicit feedback
endpoint 0x81, and mono capture endpoint 0x82. Both advertise 48/96 kHz.
I have not tested the same ordering at 96 kHz.

Recovery and limitations
------------------------
Capture-first restores the microphone. In the desktop session, I verified
recovery by selecting the input-only profile, starting capture and waiting
for PCM, then restoring the duplex profile while capture remained open.
Afterward, microphone audio and the mixer meter worked.

An application-level capture pin can protect that application's own output
links, but cannot control earlier playback from other clients. I did not
have boot-time USB tracing enabled, so I cannot identify which client
started playback first during the original failure.

Could you advise whether a device-specific initialization quirk is
appropriate, or suggest a targeted trace/test to distinguish the relevant
USB setup operations? I can test a proposed fix on this physical device.

Thank you,
James Barnette

[-- Attachment #2: reproduce-xlrdock-startup.py --]
[-- Type: text/plain, Size: 3686 bytes --]

"""Compare raw ALSA duplex startup orders; interrupt only the selected XLR Dock.

Run: python scripts/reproduce-xlrdock-startup.py --card 4
No audio is saved. Original PipeWire profile is restored, capture first.
"""
import argparse
import json
from pathlib import Path
import subprocess
import time

parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--card", required=True, type=int)
args = parser.parse_args()
device = Path(f"/sys/class/sound/card{args.card}/device").resolve(strict=True)
usb = next(p for p in device.parents if (p / "idVendor").exists())
assert (usb / "idVendor").read_text().strip() == "0fd9"
assert (usb / "idProduct").read_text().strip() == "00a6", "Only legacy XLR Dock is supported"
cards = json.loads(subprocess.check_output(["pactl", "-f", "json", "list", "cards"]))
card = next(c for c in cards if c["properties"].get("alsa.card") == str(args.card))
profile = card["active_profile"]
assert "input:mono-fallback" in profile, "An active mono capture profile is required"


def select_profile(name):
    subprocess.run(["pactl", "set-card-profile", card["name"], name], check=True)
    time.sleep(0.4)


def stop(process):
    if process and process.poll() is None:
        process.terminate()
        process.wait(timeout=5)


def pcm(command, channels, *extra):
    return subprocess.Popen(
        [command, "-q", "-D", f"hw:{args.card},0", "-t", "raw", "-f", "S24_3LE",
         "-r", "48000", "-c", str(channels), *extra],
        stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0,
    )


try:
    select_profile("off")
    for order in ["playback-first", "capture-first"] * 2:
        capture = playback = None
        try:
            prefix = b""
            if order == "playback-first":
                playback = pcm("aplay", 2, "/dev/zero")
                time.sleep(0.3)
                capture = pcm("arecord", 1, "-d", "2")
            else:
                capture = pcm("arecord", 1, "-d", "2")
                prefix = capture.stdout.read(4800 * 3)
                assert prefix, "Capture failed before playback started"
                playback = pcm("aplay", 2, "/dev/zero")
            data, error = capture.communicate(timeout=5)
            assert capture.returncode == 0, error.decode()
            assert playback.poll() is None, playback.stderr.read().decode()
            data = prefix + data
            samples = [int.from_bytes(data[i:i+3], "little", signed=True)
                       for i in range(0, len(data) - 2, 3)]
            assert len(samples) == 96000, f"Short capture: {len(samples)} samples"
            print(json.dumps({"order": order, "samples": len(samples),
                              "nonzero": sum(v != 0 for v in samples),
                              "peak": max(map(abs, samples)) / 8388608}), flush=True)
        finally:
            stop(capture)
            stop(playback)
        time.sleep(0.3)
finally:
    capture = None
    try:
        select_profile("input:mono-fallback")
        sources = json.loads(subprocess.check_output(["pactl", "-f", "json", "list", "sources"]))
        source = next(s["name"] for s in sources
                      if s["properties"].get("alsa.card") == str(args.card)
                      and not s["name"].endswith(".monitor"))
        capture = subprocess.Popen(["parec", f"--device={source}", "--format=s16le",
                                    "--rate=48000", "--channels=1"],
                                   stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, bufsize=0)
        assert capture.stdout.read(9600), "Could not prime capture for recovery"
    finally:
        select_profile(profile)
        stop(capture)

[-- Attachment #3: xlr-dock-startup-results.json --]
[-- Type: application/json, Size: 3348 bytes --]

[-- Attachment #4: xlr-dock-usb-descriptors.txt --]
[-- Type: text/plain, Size: 10418 bytes --]


Bus [redacted] Device [redacted]: ID 0fd9:00a6 Elgato Systems GmbH Elgato XLR Dock
Negotiated speed: Full Speed (12Mbps)
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 [unknown]
  bDeviceSubClass         0 [unknown]
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x0fd9 Elgato Systems GmbH
  idProduct          0x00a6 Elgato XLR Dock
  bcdDevice            2.10
  iManufacturer           1 Elgato Systems
  iProduct                2 Elgato XLR Dock
  iSerial                 3 [redacted]
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength       0x00f9
    bNumInterfaces          5
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         1 Audio
      bInterfaceSubClass      1 Control Device
      bInterfaceProtocol      0 
      iInterface              0 
      AudioControl Interface Descriptor:
        bLength                10
        bDescriptorType        36
        bDescriptorSubtype      1 (HEADER)
        bcdADC               1.00
        wTotalLength       0x004c
        bInCollection           2
        baInterfaceNr(0)        1
        baInterfaceNr(1)        2
      AudioControl Interface Descriptor:
        bLength                12
        bDescriptorType        36
        bDescriptorSubtype      2 (INPUT_TERMINAL)
        bTerminalID             1
        wTerminalType      0x0101 USB Streaming
        bAssocTerminal          0
        bNrChannels             2
        wChannelConfig     0x0003
          Left Front (L)
          Right Front (R)
        iChannelNames           0 
        iTerminal               0 
      AudioControl Interface Descriptor:
        bLength                13
        bDescriptorType        36
        bDescriptorSubtype      6 (FEATURE_UNIT)
        bUnitID                 5
        bSourceID               1
        bControlSize            2
        bmaControls(0)     0x0003
          Mute Control
          Volume Control
        bmaControls(1)     0x0000
        bmaControls(2)     0x0000
        iFeature                0 
      AudioControl Interface Descriptor:
        bLength                 9
        bDescriptorType        36
        bDescriptorSubtype      3 (OUTPUT_TERMINAL)
        bTerminalID             2
        wTerminalType      0x0302 Headphones
        bAssocTerminal          0
        bSourceID               5
        iTerminal               0 
      AudioControl Interface Descriptor:
        bLength                12
        bDescriptorType        36
        bDescriptorSubtype      2 (INPUT_TERMINAL)
        bTerminalID             4
        wTerminalType      0x0201 Microphone
        bAssocTerminal          0
        bNrChannels             1
        wChannelConfig     0x0000
        iChannelNames           0 
        iTerminal               0 
      AudioControl Interface Descriptor:
        bLength                11
        bDescriptorType        36
        bDescriptorSubtype      6 (FEATURE_UNIT)
        bUnitID                 6
        bSourceID               4
        bControlSize            2
        bmaControls(0)     0x0003
          Mute Control
          Volume Control
        bmaControls(1)     0x0000
        iFeature                0 
      AudioControl Interface Descriptor:
        bLength                 9
        bDescriptorType        36
        bDescriptorSubtype      3 (OUTPUT_TERMINAL)
        bTerminalID             3
        wTerminalType      0x0101 USB Streaming
        bAssocTerminal          0
        bSourceID               6
        iTerminal               0 
      Endpoint Descriptor:
        bLength                 9
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0002  1x 2 bytes
        bInterval             128
        bRefresh                0
        bSynchAddress           0
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           0
      bInterfaceClass         1 Audio
      bInterfaceSubClass      2 Streaming
      bInterfaceProtocol      0 
      iInterface              0 
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       1
      bNumEndpoints           2
      bInterfaceClass         1 Audio
      bInterfaceSubClass      2 Streaming
      bInterfaceProtocol      0 
      iInterface              0 
      AudioStreaming Interface Descriptor:
        bLength                 7
        bDescriptorType        36
        bDescriptorSubtype      1 (AS_GENERAL)
        bTerminalLink           1
        bDelay                  1 frames
        wFormatTag         0x0001 PCM
      AudioStreaming Interface Descriptor:
        bLength                14
        bDescriptorType        36
        bDescriptorSubtype      2 (FORMAT_TYPE)
        bFormatType             1 (FORMAT_TYPE_I)
        bNrChannels             2
        bSubframeSize           3
        bBitResolution         24
        bSamFreqType            2 Discrete
        tSamFreq[ 0]        48000
        tSamFreq[ 1]        96000
      Endpoint Descriptor:
        bLength                 9
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            5
          Transfer Type            Isochronous
          Synch Type               Asynchronous
          Usage Type               Data
        wMaxPacketSize     0x0246  1x 582 bytes
        bInterval               1
        bRefresh                0
        bSynchAddress         129
        AudioStreaming Endpoint Descriptor:
          bLength                 7
          bDescriptorType        37
          bDescriptorSubtype      1 (EP_GENERAL)
          bmAttributes         0x01
            Sampling Frequency
          bLockDelayUnits         0 Undefined
          wLockDelay         0x0000
      Endpoint Descriptor:
        bLength                 9
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes           17
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Feedback
        wMaxPacketSize     0x0004  1x 4 bytes
        bInterval               1
        bRefresh                5
        bSynchAddress           0
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        2
      bAlternateSetting       0
      bNumEndpoints           0
      bInterfaceClass         1 Audio
      bInterfaceSubClass      2 Streaming
      bInterfaceProtocol      0 
      iInterface              0 
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        2
      bAlternateSetting       1
      bNumEndpoints           1
      bInterfaceClass         1 Audio
      bInterfaceSubClass      2 Streaming
      bInterfaceProtocol      0 
      iInterface              0 
      AudioStreaming Interface Descriptor:
        bLength                 7
        bDescriptorType        36
        bDescriptorSubtype      1 (AS_GENERAL)
        bTerminalLink           3
        bDelay                  1 frames
        wFormatTag         0x0001 PCM
      AudioStreaming Interface Descriptor:
        bLength                14
        bDescriptorType        36
        bDescriptorSubtype      2 (FORMAT_TYPE)
        bFormatType             1 (FORMAT_TYPE_I)
        bNrChannels             1
        bSubframeSize           3
        bBitResolution         24
        bSamFreqType            2 Discrete
        tSamFreq[ 0]        48000
        tSamFreq[ 1]        96000
      Endpoint Descriptor:
        bLength                 9
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            5
          Transfer Type            Isochronous
          Synch Type               Asynchronous
          Usage Type               Data
        wMaxPacketSize     0x0123  1x 291 bytes
        bInterval               1
        bRefresh                0
        bSynchAddress           0
        AudioStreaming Endpoint Descriptor:
          bLength                 7
          bDescriptorType        37
          bDescriptorSubtype      1 (EP_GENERAL)
          bmAttributes         0x01
            Sampling Frequency
          bLockDelayUnits         0 Undefined
          wLockDelay         0x0000
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        3
      bAlternateSetting       0
      bNumEndpoints           0
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass    240 [unknown]
      bInterfaceProtocol      0 
      iInterface              5 Elgato XLR Dock Controls
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        4
      bAlternateSetting       0
      bNumEndpoints           0
      bInterfaceClass       254 Application Specific Interface
      bInterfaceSubClass      1 Device Firmware Update
      bInterfaceProtocol      1 
      iInterface              4 Elgato XLR Dock DFU
      Device Firmware Upgrade Interface Descriptor:
        bLength                             9
        bDescriptorType                    33
        bmAttributes                       13
          Will Detach
          Manifestation Tolerant
          Upload Unsupported
          Download Supported
        wDetachTimeout                    240 milliseconds
        wTransferSize                      64 bytes
        bcdDFUVersion                   1.10
Device Status:     0x0000
  (Bus Powered)

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-11-09  7:19 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <CAOUQ8cou-i0qNmFRsMxrjuGNKaBii1ggwGS-pjMuuexNJfBCWA@mail.gmail.com>
2026-11-07  9:12 ` [BUG] snd-usb-audio: Elgato XLR Dock 0fd9:00a6 capture silent when playback starts first Takashi Iwai
2026-11-08 21:04   ` James Barnette
2026-11-09  7:19     ` Takashi Iwai
2026-11-07  0:44 James Barnette

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox