Skip to contentSkip to conversation

ALSA: usb-audio: Skip broken playback feature unit on XiiSound chips

PATCH Superseded Cristiano Souza 2 participants 2 messages Nov 3 – Nov 4 Sound

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 ALSA: usb-audio: Skip broken playback feature unit on XiiSound chips

Weltrend XiiSound-based devices (040b:0897, e.g. Redragon H878 wireless headset) expose a UAC1 playback feature unit whose volume and mute controls are unusable in practice: the reported per-channel dB range is only 0..+0.39dB, yet setting any volume below the maximum mutes one or even both channels, and the mapping between logical and physical channels is unreliable. Any attempt by userspace to drive the hardware volume therefore results in muted or one-sided audio.

Skip the playback feature unit entirely so that userspace falls back to software volume, and pin the unit's master and per-channel volumes to the reported maximum with mutes cleared when the mixer is created, so the signal path always stays fully open. The capture path (microphone feature unit) is not affected and keeps working.

Tested with a Redragon H878 wireless headset on PipeWire/PulseAudio:
stereo output works on both channels at any volume, software volume applies smoothly, and mute/unmute works.

---
 sound/usb/mixer.c        | 16 ++++++++++++
 sound/usb/mixer_quirks.c | 56 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 72 insertions(+)
sound/usb/mixer.c +0 -0
index 95209ec..1423c72 100644
@@ -2277,6 +2277,22 @@ static int parse_audio_feature_unit(struct

mixer_build *state, int unitid,
/* disable non-functional volume control */ channels = 0;
break;
+ case USB_ID(0x040b, 0x0897):
+ /* Weltrend XiiSound chips (e.g. Redragon H878): the
+ * playback feature unit is broken -- its volume controls
+ * act like mute switches below the maximum value and the
+ * channel mapping is unreliable, so any hardware volume
+ * change mutes one or both channels. Skip the unit
+ * entirely so that userspace falls back to software
+ * volume; the unit is pinned to maximum/unmuted by the
+ * mixer quirk in mixer_quirks.c.
+ */
+ if ((iterm.type & 0xff00) == 0x0100) {
+ usb_audio_info(state->chip,
+ "usbmixer: skipping broken playback FU for XiiSound chip\n");
+ return 0;
+ }
+ break;

  }
sound/usb/mixer_quirks.c +0 -0
index 5eee501..fe389ee 100644
@@ -4419,6 +4419,59 @@ static int snd_djm_controls_create(struct

usb_mixer_interface *mixer,
return 0;
}

+/*
+ * Weltrend XiiSound chips (040b:0897, e.g. Redragon H878):
+ * the playback feature unit is broken -- its volume controls act like
+ * mute switches below the maximum value and the channel mapping is
+ * unreliable, so the unit is hidden from userspace entirely (see the
+ * quirk in mixer.c) and userspace falls back to software volume.
+ * Pin the unit's master and channel volumes to the reported maximum
+ * and unmute them, so the audio path stays fully open.
+ */
+static int snd_xiisound_init_fu_pins(struct usb_mixer_interface *mixer) +{
+ struct snd_usb_audio *chip = mixer->chip;
+ unsigned char buf[2];
+ int ifnum, ch, err;
+
+ if (mixer->protocol != UAC_VERSION_1)
+ return 0;
+
+ ifnum = snd_usb_ctrl_intf(chip->ctrl_intf);
+
+ /* unit 5 is the playback feature unit on these chips */
+ for (ch = 0; ch <= 2; ch++) {
+ err = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0),

+      UAC_GET_MAX,
+      USB_DIR_IN | USB_TYPE_CLASS |
+      USB_RECIP_INTERFACE,
+      (UAC_FU_VOLUME << 8) | ch,
+      (5 << 8) | ifnum, buf, 2);

+ if (err < 0)
+ return 0; /* no such unit/channels, nothing to pin */ +
+ err = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),

+      UAC_SET_CUR,
+      USB_DIR_OUT | USB_TYPE_CLASS |
+      USB_RECIP_INTERFACE,
+      (UAC_FU_VOLUME << 8) | ch,
+      (5 << 8) | ifnum, buf, 2);

+ if (err < 0)
+ return err;
+
+ buf[0] = 0; /* unmute */
+ err = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),

+      UAC_SET_CUR,
+      USB_DIR_OUT | USB_TYPE_CLASS |
+      USB_RECIP_INTERFACE,
+      (UAC_FU_MUTE << 8) | ch,
+      (5 << 8) | ifnum, buf, 1);

+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) {
int err = 0;
@@ -4432,6 +4485,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
case USB_ID(0x0644, 0x8047):
err = snd_us16x08_controls_create(mixer);
break;
+ case USB_ID(0x040b, 0x0897): /* Weltrend XiiSound (Redragon H878) */
+ err = snd_xiisound_init_fu_pins(mixer);
+ break;
case USB_ID(0x041e, 0x3020):
case USB_ID(0x041e, 0x3040):
case USB_ID(0x041e, 0x3042):
--
2.43.0

#1 lore raw
Quoted 16 lines · Cristiano Souza

Weltrend XiiSound-based devices (040b:0897, e.g. Redragon H878 wireless headset) expose a UAC1 playback feature unit whose volume and mute controls are unusable in practice: the reported per-channel dB range is only 0..+0.39dB, yet setting any volume below the maximum mutes one or even both channels, and the mapping between logical and physical channels is unreliable. Any attempt by userspace to drive the hardware volume therefore results in muted or one-sided audio.

Skip the playback feature unit entirely so that userspace falls back to software volume, and pin the unit's master and per-channel volumes to the reported maximum with mutes cleared when the mixer is created, so the signal path always stays fully open. The capture path (microphone feature unit) is not affected and keeps working.

Quoted 6 lines

Tested with a Redragon H878 wireless headset on PipeWire/PulseAudio:
stereo output works on both channels at any volume, software volume applies smoothly, and mute/unmute works.

The patch looks inapplicable likely because your mailer broke the tabs and spaces. Also, the submission for a kernel patch should go to [email protected], not alsa-devel ML.
Please fix your mailer setup and/or try submission via git send-email.

But, before that, about the code change: I wonder whether you can achieve it more simply by defining a usbmix_name_map in mixer_maps.c? It can be used to skip unsupported units, too.

thanks,

Takashi