public inbox for linux-doc@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID
@ 2026-03-01 21:37 Rong Zhang
  2026-03-01 21:37 ` [PATCH 1/9] Revert "ALSA: usb: Increase volume range that triggers a warning" Rong Zhang
                   ` (9 more replies)
  0 siblings, 10 replies; 17+ messages in thread
From: Rong Zhang @ 2026-03-01 21:37 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai
  Cc: Rong Zhang, Jonathan Corbet, Shuah Khan, Cryolitia PukNgae,
	Arun Raghavan, linux-sound, linux-doc, linux-kernel,
	Icenowy Zheng

Some quirky devices tune their volume by linearly tuning the voltage
level (linear volume). In other words, such devices has a linear TLV
mapping of DECLARE_TLV_DB_LINEAR(scale, TLV_DB_GAIN_MUTE, 0).

The series mainly adds quirk flags MIXER_PLAYBACK_LINEAR_VOL and
MIXER_CAPTURE_LINEAR_VOL to represent this case respectively for
playback and capture mixers. Afterward, apply these quirk flags on them.

Some MV-SILICON devices with these quirks also have another quirk: VID
conflicts with Focusrite Novation (0x1235). Hence, add support for
string-descriptor-based quirk table entries and define an entry for MV-
SILICON to deconflict them.

Some improvements to the logic of volume range checks is also included
in the series to help identify quirky devices with linear volume.

Rong Zhang (9):
  Revert "ALSA: usb: Increase volume range that triggers a warning"
  ALSA: usb-audio: Add helper function for volume range checks
  ALSA: usb-audio: Improve volume range checks
  ALSA: usb-audio: Support string-descriptor-based quirk table entry
  ALSA: usb-audio: Deconflict VID between Focusrite Novation &
    MV-SILICON
  ALSA: doc: Add doc for QUIRK_FLAG_SKIP_IFACE_SETUP of snd-usb-audio
  ALSA: usb-audio: Add QUIRK_FLAG_MIXER_{PLAYBACK,CAPTURE}_LINEAR_VOL
  ALSA: usb-audio: Add linear volume quirk for Hotone Audio Pulze Mini
  ALSA: usb-audio: Apply linear volume quirk on MV-SILICON devices

 Documentation/sound/alsa-configuration.rst | 11 +++
 sound/usb/mixer.c                          | 84 ++++++++++++++++----
 sound/usb/mixer_quirks.c                   | 56 ++++++++++++++
 sound/usb/quirks.c                         | 89 ++++++++++++++++++++++
 sound/usb/usbaudio.h                       | 12 +++
 5 files changed, 237 insertions(+), 15 deletions(-)


base-commit: 39c633261414f12cb533a8b802ee57e2d2e3c482
-- 
2.51.0


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

* [PATCH 1/9] Revert "ALSA: usb: Increase volume range that triggers a warning"
  2026-03-01 21:37 [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID Rong Zhang
@ 2026-03-01 21:37 ` Rong Zhang
  2026-03-01 21:37 ` [PATCH 2/9] ALSA: usb-audio: Add helper function for volume range checks Rong Zhang
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Rong Zhang @ 2026-03-01 21:37 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai
  Cc: Rong Zhang, Jonathan Corbet, Shuah Khan, Cryolitia PukNgae,
	Arun Raghavan, linux-sound, linux-doc, linux-kernel,
	Icenowy Zheng, stable

UAC uses 2 bytes to store volume values, so the maximum volume range is
0xFFFF (65535, val = -32768/32767/1).

The reverted commit bumpped the range of triggering the warning to >
65535, effectively making the range check a no-op. It didn't fix
anything but covered any potential problems and deviated from the
original intention of the range check.

This reverts commit 6b971191fcfc9e3c2c0143eea22534f1f48dbb62.

Fixes: 6b971191fcfc ("ALSA: usb: Increase volume range that triggers a warning")
Cc: stable@vger.kernel.org
Signed-off-by: Rong Zhang <i@rong.moe>
---
 sound/usb/mixer.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index ac8c71ba94834..df0d3df9c7ece 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1813,10 +1813,11 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
 
 	range = (cval->max - cval->min) / cval->res;
 	/*
-	 * There are definitely devices with a range of ~20,000, so let's be
-	 * conservative and allow for a bit more.
+	 * Are there devices with volume range more than 255? I use a bit more
+	 * to be sure. 384 is a resolution magic number found on Logitech
+	 * devices. It will definitively catch all buggy Logitech devices.
 	 */
-	if (range > 65535) {
+	if (range > 384) {
 		usb_audio_warn(mixer->chip,
 			       "Warning! Unlikely big volume range (=%u), cval->res is probably wrong.",
 			       range);
-- 
2.51.0


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

* [PATCH 2/9] ALSA: usb-audio: Add helper function for volume range checks
  2026-03-01 21:37 [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID Rong Zhang
  2026-03-01 21:37 ` [PATCH 1/9] Revert "ALSA: usb: Increase volume range that triggers a warning" Rong Zhang
@ 2026-03-01 21:37 ` Rong Zhang
  2026-03-01 21:37 ` [PATCH 3/9] ALSA: usb-audio: Improve " Rong Zhang
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Rong Zhang @ 2026-03-01 21:37 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai
  Cc: Rong Zhang, Jonathan Corbet, Shuah Khan, Cryolitia PukNgae,
	Arun Raghavan, linux-sound, linux-doc, linux-kernel,
	Icenowy Zheng

When a potentially insane volume range is found, the volume control
parameters will be printed in WARN level instead of DEBUG level.
Currently, it's done by emitting a open-coded usb_audio_warn() in the
corresponding check.

The following changes are about to add more checks against volumen
ranges. As the first step, extract the current check logic into a helper
function to improve readability.

No functional change intended.

Signed-off-by: Rong Zhang <i@rong.moe>
---
 sound/usb/mixer.c | 43 +++++++++++++++++++++++++++----------------
 1 file changed, 27 insertions(+), 16 deletions(-)

diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index df0d3df9c7ece..f52ca0d7e6653 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1660,6 +1660,27 @@ static const struct usb_feature_control_info *get_feature_control_info(int contr
 	return NULL;
 }
 
+static bool check_insane_volume_range(struct usb_mixer_interface *mixer,
+				      struct snd_kcontrol *kctl,
+				      struct usb_mixer_elem_info *cval)
+{
+	int range = (cval->max - cval->min) / cval->res;
+
+	/*
+	 * Are there devices with volume range more than 255? I use a bit more
+	 * to be sure. 384 is a resolution magic number found on Logitech
+	 * devices. It will definitively catch all buggy Logitech devices.
+	 */
+	if (range > 384) {
+		usb_audio_warn(mixer->chip,
+			       "Warning! Unlikely big volume range (=%u), cval->res is probably wrong.",
+			       range);
+		return true;
+	}
+
+	return false;
+}
+
 static void __build_feature_ctl(struct usb_mixer_interface *mixer,
 				const struct usbmix_name_map *imap,
 				unsigned int ctl_mask, int control,
@@ -1673,7 +1694,6 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
 	struct snd_kcontrol *kctl;
 	struct usb_mixer_elem_info *cval;
 	const struct usbmix_name_map *map;
-	unsigned int range;
 
 	if (control == UAC_FU_GRAPHIC_EQUALIZER) {
 		/* FIXME: not supported yet */
@@ -1811,25 +1831,16 @@ static void __build_feature_ctl(struct usb_mixer_interface *mixer,
 
 	snd_usb_mixer_fu_apply_quirk(mixer, cval, unitid, kctl);
 
-	range = (cval->max - cval->min) / cval->res;
-	/*
-	 * Are there devices with volume range more than 255? I use a bit more
-	 * to be sure. 384 is a resolution magic number found on Logitech
-	 * devices. It will definitively catch all buggy Logitech devices.
-	 */
-	if (range > 384) {
-		usb_audio_warn(mixer->chip,
-			       "Warning! Unlikely big volume range (=%u), cval->res is probably wrong.",
-			       range);
-		usb_audio_warn(mixer->chip,
-			       "[%d] FU [%s] ch = %d, val = %d/%d/%d",
+	if (check_insane_volume_range(mixer, kctl, cval)) {
+		usb_audio_warn(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
 			       cval->head.id, kctl->id.name, cval->channels,
 			       cval->min, cval->max, cval->res);
+	} else {
+		usb_audio_dbg(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
+			      cval->head.id, kctl->id.name, cval->channels,
+			      cval->min, cval->max, cval->res);
 	}
 
-	usb_audio_dbg(mixer->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
-		      cval->head.id, kctl->id.name, cval->channels,
-		      cval->min, cval->max, cval->res);
 	snd_usb_mixer_add_control(&cval->head, kctl);
 }
 
-- 
2.51.0


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

* [PATCH 3/9] ALSA: usb-audio: Improve volume range checks
  2026-03-01 21:37 [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID Rong Zhang
  2026-03-01 21:37 ` [PATCH 1/9] Revert "ALSA: usb: Increase volume range that triggers a warning" Rong Zhang
  2026-03-01 21:37 ` [PATCH 2/9] ALSA: usb-audio: Add helper function for volume range checks Rong Zhang
@ 2026-03-01 21:37 ` Rong Zhang
  2026-03-01 21:37 ` [PATCH 4/9] ALSA: usb-audio: Support string-descriptor-based quirk table entry Rong Zhang
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Rong Zhang @ 2026-03-01 21:37 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai
  Cc: Rong Zhang, Jonathan Corbet, Shuah Khan, Cryolitia PukNgae,
	Arun Raghavan, linux-sound, linux-doc, linux-kernel,
	Icenowy Zheng

Currently the volume range check is only meant to discover quirky
microphone on webcam devices and facing these issues:

- The check is only meaningful for dB volume, but it doesn't check if
  the TLV callback is the corresponding one
- A common quirky pattern "val = 0/100/1" doesn't trigger any warning
- Some modern devices trigger the check, but they are legit
- The warning message doesn't apply to some quirky messages with linear
  volume
- The term "range" in the warning message is confusing. At readers'
  first glance it should be (max - min), but it turns out to be
  ((max - min) / res)

Solve these issues by improving the checking logic to:

- Ignore mixers with non-dB TLV
- Warn on unlikely small volume ranges (max - min < 256)
- Add some heuristics to determine if the volume range is unlikely big
- Rephrase the warning message to mention linear volume
- Rephrase the warning message in correct wording

Signed-off-by: Rong Zhang <i@rong.moe>
---
 sound/usb/mixer.c | 54 +++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 48 insertions(+), 6 deletions(-)

diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index f52ca0d7e6653..7007e0c9489b4 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1664,20 +1664,62 @@ static bool check_insane_volume_range(struct usb_mixer_interface *mixer,
 				      struct snd_kcontrol *kctl,
 				      struct usb_mixer_elem_info *cval)
 {
-	int range = (cval->max - cval->min) / cval->res;
+	int range, steps, threshold;
 
 	/*
-	 * Are there devices with volume range more than 255? I use a bit more
-	 * to be sure. 384 is a resolution magic number found on Logitech
-	 * devices. It will definitively catch all buggy Logitech devices.
+	 * If a device quirk has overrode our TLV callback, no warning should
+	 * be generated since our checks are only meaningful for dB volume.
 	 */
-	if (range > 384) {
+	if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) ||
+	    kctl->tlv.c != snd_usb_mixer_vol_tlv)
+		return false;
+
+	/*
+	 * Meaningless volume control capability (<1dB). This should cover
+	 * devices mapping their volume to val = 0/100/1, which are very likely
+	 * to be quirky.
+	 */
+	range = cval->max - cval->min;
+	if (range < 256) {
 		usb_audio_warn(mixer->chip,
-			       "Warning! Unlikely big volume range (=%u), cval->res is probably wrong.",
+			       "Warning! Unlikely small volume range (=%u), linear volume or custom curve?",
 			       range);
 		return true;
 	}
 
+	steps = range / cval->res;
+
+	/*
+	 * There are definitely devices with ~20,000 ranges (e.g., HyperX Cloud
+	 * III with val = -18944/0/1), so we use some heuristics here:
+	 *
+	 * min < 0 < max: Attenuator + amplifier? Likely to be sane
+	 *
+	 * min < 0 = max: DSP? Voltage attenuator with FW conversion to dB?
+	 * Likely to be sane
+	 *
+	 * min < max < 0: Measured values? Neutral
+	 *
+	 * min = 0 < max: Oversimplified FW conversion? Linear volume? Likely to
+	 * be quirky (e.g., MV-SILICON)
+	 *
+	 * 0 < min < max: Amplifier with fixed gains? Likely to be quirky
+	 * (e.g., Logitech webcam)
+	 */
+	if (cval->min < 0 && 0 <= cval->max)
+		threshold = 24576; /* 65535 * (3 / 8) */
+	else if (cval->min < cval->max && cval->max < 0)
+		threshold = 1024;
+	else
+		threshold = 384;
+
+	if (steps > threshold) {
+		usb_audio_warn(mixer->chip,
+			       "Warning! Unlikely big volume step count (=%u), linear volume or wrong cval->res?",
+			       steps);
+		return true;
+	}
+
 	return false;
 }
 
-- 
2.51.0


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

* [PATCH 4/9] ALSA: usb-audio: Support string-descriptor-based quirk table entry
  2026-03-01 21:37 [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID Rong Zhang
                   ` (2 preceding siblings ...)
  2026-03-01 21:37 ` [PATCH 3/9] ALSA: usb-audio: Improve " Rong Zhang
@ 2026-03-01 21:37 ` Rong Zhang
  2026-03-02  9:54   ` Takashi Iwai
  2026-03-05  6:13   ` Terry Junge
  2026-03-01 21:37 ` [PATCH 5/9] ALSA: usb-audio: Deconflict VID between Focusrite Novation & MV-SILICON Rong Zhang
                   ` (5 subsequent siblings)
  9 siblings, 2 replies; 17+ messages in thread
From: Rong Zhang @ 2026-03-01 21:37 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai
  Cc: Rong Zhang, Jonathan Corbet, Shuah Khan, Cryolitia PukNgae,
	Arun Raghavan, linux-sound, linux-doc, linux-kernel,
	Icenowy Zheng

Some quirky devices do not have a unique VID/PID. Matching them using
DEVICE_FLG() or VENDOR_FLG() may result in conflicts.

Add two new macros DEVICE_STRING_FLG() and VENDOR_STRING_FLG() to match
USB string descriptors (manufacturer and/or product) in addition to VID
and/or PID, so that we can deconflict these devices safely.

No functional change intended.

Signed-off-by: Rong Zhang <i@rong.moe>
---
 sound/usb/quirks.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 82 insertions(+)

diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index c6a78efbcaa30..495dd46ce515c 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2,8 +2,10 @@
 /*
  */
 
+#include <linux/build_bug.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/string.h>
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
 #include <linux/usb/midi.h>
@@ -2135,16 +2137,39 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
 /*
  * driver behavior quirk flags
  */
+struct usb_string_match {
+	const char *manufacturer;
+	const char *product;
+};
+
 struct usb_audio_quirk_flags_table {
 	u32 id;
 	u32 flags;
+	const struct usb_string_match *usb_string_match;
 };
 
 #define DEVICE_FLG(vid, pid, _flags) \
 	{ .id = USB_ID(vid, pid), .flags = (_flags) }
 #define VENDOR_FLG(vid, _flags) DEVICE_FLG(vid, 0, _flags)
 
+/* Use as a last resort if using DEVICE_FLG() is prone to VID/PID conflicts. */
+#define DEVICE_STRING_FLG(vid, pid, _manufacturer, _product, _flags)	\
+{									\
+	.id = USB_ID(vid, pid),						\
+	.usb_string_match = &(const struct usb_string_match) {		\
+		.manufacturer = _manufacturer,				\
+		.product = _product,					\
+	},								\
+	.flags = (_flags),						\
+}
+
+/* Use as a last resort if using VENDOR_FLG() is prone to VID conflicts. */
+#define VENDOR_STRING_FLG(vid, _manufacturer, _flags)			\
+	DEVICE_STRING_FLG(vid, 0, _manufacturer, NULL, _flags)
+
 static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
+	/* Device and string descriptor matches */
+
 	/* Device matches */
 	DEVICE_FLG(0x001f, 0x0b21, /* AB13X USB Audio */
 		   QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
@@ -2414,6 +2439,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
 	DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */
 		   QUIRK_FLAG_ALIGN_TRANSFER),
 
+	/* Vendor and string descriptor matches */
+
 	/* Vendor matches */
 	VENDOR_FLG(0x045e, /* MS Lifecam */
 		   QUIRK_FLAG_GET_SAMPLE_RATE),
@@ -2558,14 +2585,69 @@ void snd_usb_apply_flag_dbg(const char *reason,
 	}
 }
 
+#define USB_STRING_SIZE 128
+
+static inline int snd_usb_get_strings(struct snd_usb_audio *chip,
+				      char *manufacturer, char *product)
+{
+	int ret;
+
+	if (manufacturer) {
+		ret = usb_string(chip->dev, chip->dev->descriptor.iManufacturer,
+				 manufacturer, USB_STRING_SIZE);
+		if (ret < 0) {
+			usb_audio_warn(chip, "failed to get manufacturer string: %d\n", ret);
+			return ret;
+		}
+	}
+
+	if (product) {
+		ret = usb_string(chip->dev, chip->dev->descriptor.iProduct,
+				 product, USB_STRING_SIZE);
+		if (ret < 0) {
+			usb_audio_warn(chip, "failed to get product string: %d\n", ret);
+			return ret;
+		}
+	}
+
+	return 1; /* ok */
+}
+
 void snd_usb_init_quirk_flags_table(struct snd_usb_audio *chip)
 {
 	const struct usb_audio_quirk_flags_table *p;
+	char manufacturer[USB_STRING_SIZE];
+	char product[USB_STRING_SIZE];
+	int got_usb_strings = 0; /* <0: error, 0: pending, >0: ok */
 
 	for (p = quirk_flags_table; p->id; p++) {
 		if (chip->usb_id == p->id ||
 		    (!USB_ID_PRODUCT(p->id) &&
 		     USB_ID_VENDOR(chip->usb_id) == USB_ID_VENDOR(p->id))) {
+			if (!p->usb_string_match)
+				goto apply; /* DEVICE_FLG or VENDOR_FLG */
+
+			/* DEVICE_STRING_FLG or VENDOR_STRING_FLG */
+			if (!got_usb_strings) {
+				got_usb_strings = snd_usb_get_strings(chip,
+					p->usb_string_match->manufacturer ? manufacturer : NULL,
+					p->usb_string_match->product ? product : NULL);
+			}
+
+			BUILD_BUG_ON(!got_usb_strings);
+
+			if (got_usb_strings < 0)
+				continue;
+
+			if (p->usb_string_match->manufacturer &&
+			    strcmp(p->usb_string_match->manufacturer, manufacturer))
+				continue;
+
+			if (p->usb_string_match->product &&
+			    strcmp(p->usb_string_match->product, product))
+				continue;
+
+apply:
 			snd_usb_apply_flag_dbg("builtin table", chip, p->flags);
 			chip->quirk_flags |= p->flags;
 			return;
-- 
2.51.0


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

* [PATCH 5/9] ALSA: usb-audio: Deconflict VID between Focusrite Novation & MV-SILICON
  2026-03-01 21:37 [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID Rong Zhang
                   ` (3 preceding siblings ...)
  2026-03-01 21:37 ` [PATCH 4/9] ALSA: usb-audio: Support string-descriptor-based quirk table entry Rong Zhang
@ 2026-03-01 21:37 ` Rong Zhang
  2026-03-01 21:37 ` [PATCH 6/9] ALSA: doc: Add doc for QUIRK_FLAG_SKIP_IFACE_SETUP of snd-usb-audio Rong Zhang
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Rong Zhang @ 2026-03-01 21:37 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai
  Cc: Rong Zhang, Jonathan Corbet, Shuah Khan, Cryolitia PukNgae,
	Arun Raghavan, linux-sound, linux-doc, linux-kernel,
	Icenowy Zheng

MV-SILICON is a SoC manufacturer producing multifunctional audio SoCs.
Many budget-oriented OEM devices are built on top of them. However, some
of them are just too budget-constrained that their manufacturers didn't
even have a USB VID and simply picked a random VID.

Some OEMs unfortunately picked the VID of Focusrite Novation (0x1235),
resulting in VID conflicts as we had defined a VENDOR_FLG() for the
latter.

Add a VENDOR_STRING_FLG() for MV-SILICON to stop the matching procedure
for these quirky devices, so that quirk flags for Focusrite Novation
won't be accidentally applied on them.

Quirky device samples:

  usb 7-1: New USB device found, idVendor=1235, idProduct=0003, bcdDevice= 1.00
  usb 7-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
  usb 7-1: Product: G1
  usb 7-1: Manufacturer: MV-SILICON
  usb 7-1: SerialNumber: 20190808

  usb 7-1: New USB device found, idVendor=1235, idProduct=0003, bcdDevice= 1.00
  usb 7-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
  usb 7-1: Product: mvsilicon B1 usb audio
  usb 7-1: Manufacturer: MV-SILICON
  usb 7-1: SerialNumber: 20190808

  usb 1-1.2: New USB device found, idVendor=1235, idProduct=0002, bcdDevice= 1.00
  usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=...
  usb 1-1.2: Product: V8
  usb 1-1.2: Manufacturer: MV-SILICON
  usb 1-1.2: SerialNumber: ...
  * https://github.com/linuxhw/Dmesg/blob/main/Desktop/Others/Intel/Intel%20X79/96ED1CC44499/LINUXMINT-19.3/5.0.0-32-GENERIC/X86_64/5BE1E4C74C#L1122

  usb 2-1.6: New USB device found, idVendor=1235, idProduct=0002, bcdDevice= 1.00
  usb 2-1.6: New USB device strings: Mfr=1, Product=2, SerialNumber=...
  usb 2-1.6: Product: V9
  usb 2-1.6: Manufacturer: MV-SILICON
  usb 2-1.6: SerialNumber: ...
  * https://github.com/linuxhw/Dmesg/blob/main/Desktop/Hewlett-Packard/ProLiant/ProLiant%20ML110%20G6/79B1D707316A/KUBUNTU-21.04/5.11.0-33-GENERIC/X86_64/A43F59C4AB#L1009

Signed-off-by: Rong Zhang <i@rong.moe>
---
 sound/usb/quirks.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 495dd46ce515c..5370bcd99e9d0 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2440,6 +2440,9 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
 		   QUIRK_FLAG_ALIGN_TRANSFER),
 
 	/* Vendor and string descriptor matches */
+	VENDOR_STRING_FLG(0x1235, /* VID conflict with Focusrite Novation */
+			  "MV-SILICON",
+			  0), /* Stop matching */
 
 	/* Vendor matches */
 	VENDOR_FLG(0x045e, /* MS Lifecam */
-- 
2.51.0


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

* [PATCH 6/9] ALSA: doc: Add doc for QUIRK_FLAG_SKIP_IFACE_SETUP of snd-usb-audio
  2026-03-01 21:37 [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID Rong Zhang
                   ` (4 preceding siblings ...)
  2026-03-01 21:37 ` [PATCH 5/9] ALSA: usb-audio: Deconflict VID between Focusrite Novation & MV-SILICON Rong Zhang
@ 2026-03-01 21:37 ` Rong Zhang
  2026-03-01 21:37 ` [PATCH 7/9] ALSA: usb-audio: Add QUIRK_FLAG_MIXER_{PLAYBACK,CAPTURE}_LINEAR_VOL Rong Zhang
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Rong Zhang @ 2026-03-01 21:37 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai
  Cc: Rong Zhang, Jonathan Corbet, Shuah Khan, Cryolitia PukNgae,
	Arun Raghavan, linux-sound, linux-doc, linux-kernel,
	Icenowy Zheng

QUIRK_FLAG_SKIP_IFACE_SETUP was introduced in commit 38c322068a26
("ALSA: usb-audio: Add QUIRK_FLAG_SKIP_IFACE_SETUP") without appropriate
documentation, so add it.

Signed-off-by: Rong Zhang <i@rong.moe>
---
 Documentation/sound/alsa-configuration.rst | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/Documentation/sound/alsa-configuration.rst b/Documentation/sound/alsa-configuration.rst
index 0a4eaa7d66ddd..55b845d382368 100644
--- a/Documentation/sound/alsa-configuration.rst
+++ b/Documentation/sound/alsa-configuration.rst
@@ -2372,6 +2372,10 @@ quirk_flags
           audible volume
         * bit 25: ``mixer_capture_min_mute``
           Similar to bit 24 but for capture streams
+        * bit 26: ``skip_iface_setup``
+          Skip the probe-time interface setup (usb_set_interface,
+          init_pitch, init_sample_rate); redundant with
+          snd_usb_endpoint_prepare() at stream-open time
 
 This module supports multiple devices, autoprobe and hotplugging.
 
-- 
2.51.0


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

* [PATCH 7/9] ALSA: usb-audio: Add QUIRK_FLAG_MIXER_{PLAYBACK,CAPTURE}_LINEAR_VOL
  2026-03-01 21:37 [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID Rong Zhang
                   ` (5 preceding siblings ...)
  2026-03-01 21:37 ` [PATCH 6/9] ALSA: doc: Add doc for QUIRK_FLAG_SKIP_IFACE_SETUP of snd-usb-audio Rong Zhang
@ 2026-03-01 21:37 ` Rong Zhang
  2026-03-01 21:37 ` [PATCH 8/9] ALSA: usb-audio: Add linear volume quirk for Hotone Audio Pulze Mini Rong Zhang
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Rong Zhang @ 2026-03-01 21:37 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai
  Cc: Rong Zhang, Jonathan Corbet, Shuah Khan, Cryolitia PukNgae,
	Arun Raghavan, linux-sound, linux-doc, linux-kernel,
	Icenowy Zheng

Some quirky devices tune their volume by linearly tuning the voltage
level (linear volume). In other words, such devices has a linear TLV
mapping of DECLARE_TLV_DB_LINEAR(scale, TLV_DB_GAIN_MUTE, 0).

Add quirk flags MIXER_PLAYBACK_LINEAR_VOL and MIXER_CAPTURE_LINEAR_VOL
to represent this case respectively for playback and capture mixers.

No functional change intended.

Signed-off-by: Rong Zhang <i@rong.moe>
---
 Documentation/sound/alsa-configuration.rst |  7 +++++
 sound/usb/mixer_quirks.c                   | 34 ++++++++++++++++++++++
 sound/usb/quirks.c                         |  2 ++
 sound/usb/usbaudio.h                       | 12 ++++++++
 4 files changed, 55 insertions(+)

diff --git a/Documentation/sound/alsa-configuration.rst b/Documentation/sound/alsa-configuration.rst
index 55b845d382368..f75f087639415 100644
--- a/Documentation/sound/alsa-configuration.rst
+++ b/Documentation/sound/alsa-configuration.rst
@@ -2376,6 +2376,13 @@ quirk_flags
           Skip the probe-time interface setup (usb_set_interface,
           init_pitch, init_sample_rate); redundant with
           snd_usb_endpoint_prepare() at stream-open time
+        * bit 27: ``mixer_playback_linear_vol``
+          Set linear volume mapping for devices where the playback volume
+          control value is mapped to voltage (instead of dB) level linearly.
+          In short: ``x(raw) = (raw - raw_min) / (raw_max - raw_min)``;
+          ``V(x) = k * x``; ``dB(x) = 20 * log10(x)``. Overrides bit 24
+        * bit 28: ``mixer_capture_linear_vol``
+          Similar to bit 27 but for capture streams. Overrides bit 25
 
 This module supports multiple devices, autoprobe and hotplugging.
 
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 11e205da7964d..539044c0c6440 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -4634,6 +4634,25 @@ static void snd_fix_plt_name(struct snd_usb_audio *chip,
 	usb_audio_dbg(chip, "something wrong in kctl name %s\n", id->name);
 }
 
+static void snd_usb_mixer_fu_quirk_linear_scale(struct usb_mixer_interface *mixer,
+						struct usb_mixer_elem_info *cval,
+						struct snd_kcontrol *kctl)
+{
+	static const DECLARE_TLV_DB_LINEAR(scale, TLV_DB_GAIN_MUTE, 0);
+
+	if (cval->min_mute) {
+		/*
+		 * We are clearing SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
+		 * resulting in min_mute being a no-op.
+		 */
+		usb_audio_warn(mixer->chip, "LINEAR_VOL overrides MIN_MUTE\n");
+	}
+
+	kctl->tlv.p = scale;
+	kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+	kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+}
+
 void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
 				  struct usb_mixer_elem_info *cval, int unitid,
 				  struct snd_kcontrol *kctl)
@@ -4660,6 +4679,21 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
 				       "applying capture min mute quirk\n");
 			cval->min_mute = 1;
 		}
+
+	if (mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL)
+		if (cval->control == UAC_FU_VOLUME && strstr(kctl->id.name, "Playback")) {
+			usb_audio_info(mixer->chip,
+				       "applying playback linear volume quirk\n");
+			snd_usb_mixer_fu_quirk_linear_scale(mixer, cval, kctl);
+		}
+
+	if (mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL)
+		if (cval->control == UAC_FU_VOLUME && strstr(kctl->id.name, "Capture")) {
+			usb_audio_info(mixer->chip,
+				       "applying capture linear volume quirk\n");
+			snd_usb_mixer_fu_quirk_linear_scale(mixer, cval, kctl);
+		}
+
 	/* ALSA-ify some Plantronics headset control names */
 	if (USB_ID_VENDOR(mixer->chip->usb_id) == 0x047f &&
 	    (cval->control == UAC_FU_MUTE || cval->control == UAC_FU_VOLUME))
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 5370bcd99e9d0..2327288e2b20f 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2540,6 +2540,8 @@ static const char *const snd_usb_audio_quirk_flag_names[] = {
 	QUIRK_STRING_ENTRY(MIXER_PLAYBACK_MIN_MUTE),
 	QUIRK_STRING_ENTRY(MIXER_CAPTURE_MIN_MUTE),
 	QUIRK_STRING_ENTRY(SKIP_IFACE_SETUP),
+	QUIRK_STRING_ENTRY(MIXER_PLAYBACK_LINEAR_VOL),
+	QUIRK_STRING_ENTRY(MIXER_CAPTURE_LINEAR_VOL),
 	NULL
 };
 
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index 085530cf62d92..58fd07f8c3c97 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -228,6 +228,14 @@ extern bool snd_usb_skip_validation;
  *  Skip the probe-time interface setup (usb_set_interface,
  *  init_pitch, init_sample_rate); redundant with
  *  snd_usb_endpoint_prepare() at stream-open time
+ * QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL
+ *  Set linear volume mapping for devices where the playback volume control
+ *  value is mapped to voltage (instead of dB) level linearly. In short:
+ *  x(raw) = (raw - raw_min) / (raw_max - raw_min); V(x) = k * x;
+ *  dB(x) = 20 * log10(x). Overrides QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE
+ * QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL
+ *  Similar to QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL, but for capture streams.
+ *  Overrides QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE
  */
 
 enum {
@@ -258,6 +266,8 @@ enum {
 	QUIRK_TYPE_MIXER_PLAYBACK_MIN_MUTE	= 24,
 	QUIRK_TYPE_MIXER_CAPTURE_MIN_MUTE	= 25,
 	QUIRK_TYPE_SKIP_IFACE_SETUP		= 26,
+	QUIRK_TYPE_MIXER_PLAYBACK_LINEAR_VOL	= 27,
+	QUIRK_TYPE_MIXER_CAPTURE_LINEAR_VOL	= 28,
 /* Please also edit snd_usb_audio_quirk_flag_names */
 };
 
@@ -290,5 +300,7 @@ enum {
 #define QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE	QUIRK_FLAG(MIXER_PLAYBACK_MIN_MUTE)
 #define QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE	QUIRK_FLAG(MIXER_CAPTURE_MIN_MUTE)
 #define QUIRK_FLAG_SKIP_IFACE_SETUP		QUIRK_FLAG(SKIP_IFACE_SETUP)
+#define QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL	QUIRK_FLAG(MIXER_PLAYBACK_LINEAR_VOL)
+#define QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL	QUIRK_FLAG(MIXER_CAPTURE_LINEAR_VOL)
 
 #endif /* __USBAUDIO_H */
-- 
2.51.0


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

* [PATCH 8/9] ALSA: usb-audio: Add linear volume quirk for Hotone Audio Pulze Mini
  2026-03-01 21:37 [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID Rong Zhang
                   ` (6 preceding siblings ...)
  2026-03-01 21:37 ` [PATCH 7/9] ALSA: usb-audio: Add QUIRK_FLAG_MIXER_{PLAYBACK,CAPTURE}_LINEAR_VOL Rong Zhang
@ 2026-03-01 21:37 ` Rong Zhang
  2026-03-01 21:37 ` [PATCH 9/9] ALSA: usb-audio: Apply linear volume quirk on MV-SILICON devices Rong Zhang
  2026-03-02  9:59 ` [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID Takashi Iwai
  9 siblings, 0 replies; 17+ messages in thread
From: Rong Zhang @ 2026-03-01 21:37 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai
  Cc: Rong Zhang, Jonathan Corbet, Shuah Khan, Cryolitia PukNgae,
	Arun Raghavan, linux-sound, linux-doc, linux-kernel,
	Icenowy Zheng

Hotone Audio Pulze Mini is a modeling amplifier with UAC interface. Its
Playback and Capture mixers use linear volume with val = 0/100/1.

Add a quirk table entry matching VID/PID=0x84ef/0x0082 and applying
linear volume quirk flags, so that it can work properly.

Quirky device sample:

  usb 7-1: New USB device found, idVendor=84ef, idProduct=0082, bcdDevice= 1.03
  usb 7-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
  usb 7-1: Product: Pulze Mini
  usb 7-1: Manufacturer: Hotone Audio
  usb 7-1: SerialNumber: 20240807

Signed-off-by: Rong Zhang <i@rong.moe>
---
 sound/usb/quirks.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 2327288e2b20f..ec809b7eb5bea 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2438,6 +2438,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
 		   QUIRK_FLAG_ALIGN_TRANSFER),
 	DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */
 		   QUIRK_FLAG_ALIGN_TRANSFER),
+	DEVICE_FLG(0x84ef, 0x0082, /* Hotone Audio Pulze Mini */
+		   QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL | QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL),
 
 	/* Vendor and string descriptor matches */
 	VENDOR_STRING_FLG(0x1235, /* VID conflict with Focusrite Novation */
-- 
2.51.0


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

* [PATCH 9/9] ALSA: usb-audio: Apply linear volume quirk on MV-SILICON devices
  2026-03-01 21:37 [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID Rong Zhang
                   ` (7 preceding siblings ...)
  2026-03-01 21:37 ` [PATCH 8/9] ALSA: usb-audio: Add linear volume quirk for Hotone Audio Pulze Mini Rong Zhang
@ 2026-03-01 21:37 ` Rong Zhang
  2026-03-02  9:59 ` [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID Takashi Iwai
  9 siblings, 0 replies; 17+ messages in thread
From: Rong Zhang @ 2026-03-01 21:37 UTC (permalink / raw)
  To: Jaroslav Kysela, Takashi Iwai
  Cc: Rong Zhang, Jonathan Corbet, Shuah Khan, Cryolitia PukNgae,
	Arun Raghavan, linux-sound, linux-doc, linux-kernel,
	Icenowy Zheng

MV-SILICON is a SoC manufacturer producing multifunctional audio SoCs.
Many devices built on top of their SDK share a common quirk that the
Playback and Capture mixers use linear volume with val = 0/4096/1.

The SDK seems to always report "MV-SILICON" for manufacturer string.
Hence, match it so that we don't need to define quirk table entries
separately for each devices. The "val = 0/4096/1" pattern is also
checked against before applying the quirk, in order that the quirk won't
accidentally break unseen variants.

Quirky device samples:

  usb 7-1: New USB device found, idVendor=1235, idProduct=0003, bcdDevice= 1.00
  usb 7-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
  usb 7-1: Product: G1
  usb 7-1: Manufacturer: MV-SILICON
  usb 7-1: SerialNumber: 20190808

  usb 7-1: New USB device found, idVendor=1235, idProduct=0003, bcdDevice= 1.00
  usb 7-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
  usb 7-1: Product: mvsilicon B1 usb audio
  usb 7-1: Manufacturer: MV-SILICON
  usb 7-1: SerialNumber: 20190808

  usb 5-1.4: New USB device found, idVendor=8888, idProduct=1719, bcdDevice= 1.00
  usb 5-1.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
  usb 5-1.4: Product: HF310 USB Audio
  usb 5-1.4: Manufacturer: MV-SILICON
  usb 5-1.4: SerialNumber: 20190808

  usb 7-1: New USB device found, idVendor=2717, idProduct=5086, bcdDevice= 1.00
  usb 7-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
  usb 7-1: Product: Redmi 电脑音箱
  usb 7-1: Manufacturer: MV-SILICON
  usb 7-1: SerialNumber: 20190808

  usb 2-1.2: New USB device found, idVendor=3142, idProduct=a601, bcdDevice= 1.00
  usb 2-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
  usb 2-1.2: Product: fifine Microphone
  usb 2-1.2: Manufacturer: MV-SILICON
  usb 2-1.2: SerialNumber: 20190808
  * https://forum.ubuntu-it.org/viewtopic.php?t=659345

Signed-off-by: Rong Zhang <i@rong.moe>
---
 sound/usb/mixer_quirks.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 539044c0c6440..e97814dc90259 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -4588,6 +4588,24 @@ static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer,
 	}
 }
 
+static void snd_usb_mv_silicon_quirks(struct usb_mixer_interface *mixer,
+				      struct usb_mixer_elem_info *cval,
+				      struct snd_kcontrol *kctl)
+{
+	if (cval->min == 0 && cval->max == 4096 && cval->res == 1) {
+		/* The final effects will be printed later. */
+		usb_audio_info(mixer->chip, "applying MV-SILICON quirks (0/4096/1 variant)\n");
+
+		/* Respect MIN_MUTE set by module parameters. */
+		if (!(mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE))
+			mixer->chip->quirk_flags |= QUIRK_FLAG_MIXER_PLAYBACK_LINEAR_VOL;
+		if (!(mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE))
+			mixer->chip->quirk_flags |= QUIRK_FLAG_MIXER_CAPTURE_LINEAR_VOL;
+	} else {
+		usb_audio_dbg(mixer->chip, "not applying MV-SILICON quirks on unknown variant");
+	}
+}
+
 /*
  * Some Plantronics headsets have control names that don't meet ALSA naming
  * standards. This function fixes nonstandard source names. By the time
@@ -4664,6 +4682,10 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
 		break;
 	}
 
+	if (cval->control == UAC_FU_VOLUME &&
+	    !strncmp(mixer->chip->card->longname, "MV-SILICON", 10))
+		snd_usb_mv_silicon_quirks(mixer, cval, kctl);
+
 	/* lowest playback value is muted on some devices */
 	if (mixer->chip->quirk_flags & QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE)
 		if (strstr(kctl->id.name, "Playback")) {
-- 
2.51.0


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

* Re: [PATCH 4/9] ALSA: usb-audio: Support string-descriptor-based quirk table entry
  2026-03-01 21:37 ` [PATCH 4/9] ALSA: usb-audio: Support string-descriptor-based quirk table entry Rong Zhang
@ 2026-03-02  9:54   ` Takashi Iwai
  2026-03-02 12:31     ` Rong Zhang
  2026-03-05  6:13   ` Terry Junge
  1 sibling, 1 reply; 17+ messages in thread
From: Takashi Iwai @ 2026-03-02  9:54 UTC (permalink / raw)
  To: Rong Zhang
  Cc: Jaroslav Kysela, Takashi Iwai, Jonathan Corbet, Shuah Khan,
	Cryolitia PukNgae, Arun Raghavan, linux-sound, linux-doc,
	linux-kernel, Icenowy Zheng

On Sun, 01 Mar 2026 22:37:20 +0100,
Rong Zhang wrote:
> @@ -2558,14 +2585,69 @@ void snd_usb_apply_flag_dbg(const char *reason,
>  	}
>  }
>  
> +#define USB_STRING_SIZE 128
> +
> +static inline int snd_usb_get_strings(struct snd_usb_audio *chip,
> +				      char *manufacturer, char *product)
> +{
> +	int ret;
> +
> +	if (manufacturer) {
> +		ret = usb_string(chip->dev, chip->dev->descriptor.iManufacturer,
> +				 manufacturer, USB_STRING_SIZE);
> +		if (ret < 0) {
> +			usb_audio_warn(chip, "failed to get manufacturer string: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	if (product) {
> +		ret = usb_string(chip->dev, chip->dev->descriptor.iProduct,
> +				 product, USB_STRING_SIZE);
> +		if (ret < 0) {
> +			usb_audio_warn(chip, "failed to get product string: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	return 1; /* ok */
> +}
> +
>  void snd_usb_init_quirk_flags_table(struct snd_usb_audio *chip)
>  {
>  	const struct usb_audio_quirk_flags_table *p;
> +	char manufacturer[USB_STRING_SIZE];
> +	char product[USB_STRING_SIZE];

Keeping those a bit largish strings on the stack doesn't look good.
Maybe better to do kalloc with __free(kfree).

> +	int got_usb_strings = 0; /* <0: error, 0: pending, >0: ok */

I think this global flag could go wrong since...

>  	for (p = quirk_flags_table; p->id; p++) {
>  		if (chip->usb_id == p->id ||
>  		    (!USB_ID_PRODUCT(p->id) &&
>  		     USB_ID_VENDOR(chip->usb_id) == USB_ID_VENDOR(p->id))) {
> +			if (!p->usb_string_match)
> +				goto apply; /* DEVICE_FLG or VENDOR_FLG */
> +
> +			/* DEVICE_STRING_FLG or VENDOR_STRING_FLG */
> +			if (!got_usb_strings) {
> +				got_usb_strings = snd_usb_get_strings(chip,
> +					p->usb_string_match->manufacturer ? manufacturer : NULL,
> +					p->usb_string_match->product ? product : NULL);

... here you try to extract strings only once, but this retrieval
depends on the current entry.  If the current entry has only
manufacturer and the next entry has only product, it won't work.

IMO, a simpler way would be something like:

#define USB_STRING_SIZE 128

static char *snd_usb_get_string(struct snd_usb_audio *chip, int id)
{
	char *buf = kmalloc(USB_STRING_SIZE, GFP_KERNEL);
	int ret;

	if (!buf)
		return ERR_PTR(-ENOMEM);
	ret = usb_string(chip->dev, id, buf, USB_STRING_SIZE);
	if (ret < 0) {
		usb_audio_warn(chip, "failed to get string for id%d: %d\n", ret);
		kfree(buf);
		return ERR_PTR(ret);
	}
	return buf;
}

void snd_usb_init_quirk_flags_table(struct snd_usb_audio *chip)
{
	....
	char *manufacturer __free(kfree) = NULL;
	char *product __free(kfree) = NULL;

  	for (p = quirk_flags_table; p->id; p++) {
		....
		if (p->usb_string_match->manufacturer) {
			if (!manufacturer)
				manufaturer =
					snd_usb_get_string(chip,
						chip->dev->descriptor.iManufacturer);
			if (!IS_ERR_OR_NULL(manufacturer) &&
			    strcmp(p->usb_string_match->manufacturer, manufacturer))
				continue;			
		}
		if (p->usb_string_match->product) {
			if (!product)
				product =
					snd_usb_get_string(chip,
						chip->dev->descriptor.iProduct);
			if (!IS_ERR_OR_NULL(product) &&
			    strcmp(p->usb_string_match->product, product))
				continue;			
		}


thanks,

Takashi

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

* Re: [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID
  2026-03-01 21:37 [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID Rong Zhang
                   ` (8 preceding siblings ...)
  2026-03-01 21:37 ` [PATCH 9/9] ALSA: usb-audio: Apply linear volume quirk on MV-SILICON devices Rong Zhang
@ 2026-03-02  9:59 ` Takashi Iwai
  2026-03-02 12:40   ` Rong Zhang
  9 siblings, 1 reply; 17+ messages in thread
From: Takashi Iwai @ 2026-03-02  9:59 UTC (permalink / raw)
  To: Rong Zhang
  Cc: Jaroslav Kysela, Takashi Iwai, Jonathan Corbet, Shuah Khan,
	Cryolitia PukNgae, Arun Raghavan, linux-sound, linux-doc,
	linux-kernel, Icenowy Zheng

On Sun, 01 Mar 2026 22:37:16 +0100,
Rong Zhang wrote:
> 
> Some quirky devices tune their volume by linearly tuning the voltage
> level (linear volume). In other words, such devices has a linear TLV
> mapping of DECLARE_TLV_DB_LINEAR(scale, TLV_DB_GAIN_MUTE, 0).
> 
> The series mainly adds quirk flags MIXER_PLAYBACK_LINEAR_VOL and
> MIXER_CAPTURE_LINEAR_VOL to represent this case respectively for
> playback and capture mixers. Afterward, apply these quirk flags on them.
> 
> Some MV-SILICON devices with these quirks also have another quirk: VID
> conflicts with Focusrite Novation (0x1235). Hence, add support for
> string-descriptor-based quirk table entries and define an entry for MV-
> SILICON to deconflict them.
> 
> Some improvements to the logic of volume range checks is also included
> in the series to help identify quirky devices with linear volume.
> 
> Rong Zhang (9):
>   Revert "ALSA: usb: Increase volume range that triggers a warning"
>   ALSA: usb-audio: Add helper function for volume range checks
>   ALSA: usb-audio: Improve volume range checks
>   ALSA: usb-audio: Support string-descriptor-based quirk table entry
>   ALSA: usb-audio: Deconflict VID between Focusrite Novation &
>     MV-SILICON
>   ALSA: doc: Add doc for QUIRK_FLAG_SKIP_IFACE_SETUP of snd-usb-audio
>   ALSA: usb-audio: Add QUIRK_FLAG_MIXER_{PLAYBACK,CAPTURE}_LINEAR_VOL
>   ALSA: usb-audio: Add linear volume quirk for Hotone Audio Pulze Mini
>   ALSA: usb-audio: Apply linear volume quirk on MV-SILICON devices

Thanks for patches!  The logic looks good and the implementation
seems reasonable.  One slight concern was about the string matching
patch (I explained in another mail), but others look fine.

Maybe the patch to add the missing documentation for
QUIRK_FLAG_SKIP_IFACE_SETUP should be applied individually beforehand.
Could you send it a fix for 7.0?

The revert of volume range check makes sense, but maybe that should be
combined with your rest changes.


Takashi

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

* Re: [PATCH 4/9] ALSA: usb-audio: Support string-descriptor-based quirk table entry
  2026-03-02  9:54   ` Takashi Iwai
@ 2026-03-02 12:31     ` Rong Zhang
  0 siblings, 0 replies; 17+ messages in thread
From: Rong Zhang @ 2026-03-02 12:31 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Jaroslav Kysela, Takashi Iwai, Jonathan Corbet, Shuah Khan,
	Cryolitia PukNgae, Arun Raghavan, linux-sound, linux-doc,
	linux-kernel, Icenowy Zheng

Hi Takashi,

Thanks for your review.

On Mon, 2026-03-02 at 10:54 +0100, Takashi Iwai wrote:
> On Sun, 01 Mar 2026 22:37:20 +0100,
> Rong Zhang wrote:
> > @@ -2558,14 +2585,69 @@ void snd_usb_apply_flag_dbg(const char *reason,
> >  	}
> >  }
> >  
> > +#define USB_STRING_SIZE 128
> > +
> > +static inline int snd_usb_get_strings(struct snd_usb_audio *chip,
> > +				      char *manufacturer, char *product)
> > +{
> > +	int ret;
> > +
> > +	if (manufacturer) {
> > +		ret = usb_string(chip->dev, chip->dev->descriptor.iManufacturer,
> > +				 manufacturer, USB_STRING_SIZE);
> > +		if (ret < 0) {
> > +			usb_audio_warn(chip, "failed to get manufacturer string: %d\n", ret);
> > +			return ret;
> > +		}
> > +	}
> > +
> > +	if (product) {
> > +		ret = usb_string(chip->dev, chip->dev->descriptor.iProduct,
> > +				 product, USB_STRING_SIZE);
> > +		if (ret < 0) {
> > +			usb_audio_warn(chip, "failed to get product string: %d\n", ret);
> > +			return ret;
> > +		}
> > +	}
> > +
> > +	return 1; /* ok */
> > +}
> > +
> >  void snd_usb_init_quirk_flags_table(struct snd_usb_audio *chip)
> >  {
> >  	const struct usb_audio_quirk_flags_table *p;
> > +	char manufacturer[USB_STRING_SIZE];
> > +	char product[USB_STRING_SIZE];
> 
> Keeping those a bit largish strings on the stack doesn't look good.
> Maybe better to do kalloc with __free(kfree).

Makes sense.

> > +	int got_usb_strings = 0; /* <0: error, 0: pending, >0: ok */
> 
> I think this global flag could go wrong since...
> 
> >  	for (p = quirk_flags_table; p->id; p++) {
> >  		if (chip->usb_id == p->id ||
> >  		    (!USB_ID_PRODUCT(p->id) &&
> >  		     USB_ID_VENDOR(chip->usb_id) == USB_ID_VENDOR(p->id))) {
> > +			if (!p->usb_string_match)
> > +				goto apply; /* DEVICE_FLG or VENDOR_FLG */
> > +
> > +			/* DEVICE_STRING_FLG or VENDOR_STRING_FLG */
> > +			if (!got_usb_strings) {
> > +				got_usb_strings = snd_usb_get_strings(chip,
> > +					p->usb_string_match->manufacturer ? manufacturer : NULL,
> > +					p->usb_string_match->product ? product : NULL);
> 
> ... here you try to extract strings only once, but this retrieval
> depends on the current entry.  If the current entry has only
> manufacturer and the next entry has only product, it won't work.

Nice catch! Thanks. Will fix it in v2.

> IMO, a simpler way would be something like:
> 
> #define USB_STRING_SIZE 128
> 
> static char *snd_usb_get_string(struct snd_usb_audio *chip, int id)
> {
> 	char *buf = kmalloc(USB_STRING_SIZE, GFP_KERNEL);
> 	int ret;
> 
> 	if (!buf)
> 		return ERR_PTR(-ENOMEM);
> 	ret = usb_string(chip->dev, id, buf, USB_STRING_SIZE);
> 	if (ret < 0) {
> 		usb_audio_warn(chip, "failed to get string for id%d: %d\n", ret);
> 		kfree(buf);
> 		return ERR_PTR(ret);
> 	}
> 	return buf;
> }

I guess we may use usb_cache_string() so that we don't need to
kmalloc() the buffer ourselves and can get rid of USB_STRING_SIZE.

> void snd_usb_init_quirk_flags_table(struct snd_usb_audio *chip)
> {
> 	....
> 	char *manufacturer __free(kfree) = NULL;
> 	char *product __free(kfree) = NULL;
> 
>   	for (p = quirk_flags_table; p->id; p++) {
> 		....
> 		if (p->usb_string_match->manufacturer) {
> 			if (!manufacturer)
> 				manufaturer =
> 					snd_usb_get_string(chip,
> 						chip->dev->descriptor.iManufacturer);
> 			if (!IS_ERR_OR_NULL(manufacturer) &&
> 			    strcmp(p->usb_string_match->manufacturer, manufacturer))
> 				continue;			
> 		}
> 		if (p->usb_string_match->product) {
> 			if (!product)
> 				product =
> 					snd_usb_get_string(chip,
> 						chip->dev->descriptor.iProduct);
> 			if (!IS_ERR_OR_NULL(product) &&
> 			    strcmp(p->usb_string_match->product, product))
> 				continue;			
> 		}
> 
> 

Sounds neat. Will do in v2. I will also skip the warning if id == 0
since string-descriptor-based quirk table entries has nothing to do
with devices without corresponding descriptors.

Thanks,
Rong

> thanks,
> 
> Takashi

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

* Re: [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID
  2026-03-02  9:59 ` [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID Takashi Iwai
@ 2026-03-02 12:40   ` Rong Zhang
  0 siblings, 0 replies; 17+ messages in thread
From: Rong Zhang @ 2026-03-02 12:40 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Jaroslav Kysela, Takashi Iwai, Jonathan Corbet, Shuah Khan,
	Cryolitia PukNgae, Arun Raghavan, linux-sound, linux-doc,
	linux-kernel, Icenowy Zheng

Hi Takashi,

Thanks for your review.

On Mon, 2026-03-02 at 10:59 +0100, Takashi Iwai wrote:
> On Sun, 01 Mar 2026 22:37:16 +0100,
> Rong Zhang wrote:
> > 
> > Some quirky devices tune their volume by linearly tuning the voltage
> > level (linear volume). In other words, such devices has a linear TLV
> > mapping of DECLARE_TLV_DB_LINEAR(scale, TLV_DB_GAIN_MUTE, 0).
> > 
> > The series mainly adds quirk flags MIXER_PLAYBACK_LINEAR_VOL and
> > MIXER_CAPTURE_LINEAR_VOL to represent this case respectively for
> > playback and capture mixers. Afterward, apply these quirk flags on them.
> > 
> > Some MV-SILICON devices with these quirks also have another quirk: VID
> > conflicts with Focusrite Novation (0x1235). Hence, add support for
> > string-descriptor-based quirk table entries and define an entry for MV-
> > SILICON to deconflict them.
> > 
> > Some improvements to the logic of volume range checks is also included
> > in the series to help identify quirky devices with linear volume.
> > 
> > Rong Zhang (9):
> >   Revert "ALSA: usb: Increase volume range that triggers a warning"
> >   ALSA: usb-audio: Add helper function for volume range checks
> >   ALSA: usb-audio: Improve volume range checks
> >   ALSA: usb-audio: Support string-descriptor-based quirk table entry
> >   ALSA: usb-audio: Deconflict VID between Focusrite Novation &
> >     MV-SILICON
> >   ALSA: doc: Add doc for QUIRK_FLAG_SKIP_IFACE_SETUP of snd-usb-audio
> >   ALSA: usb-audio: Add QUIRK_FLAG_MIXER_{PLAYBACK,CAPTURE}_LINEAR_VOL
> >   ALSA: usb-audio: Add linear volume quirk for Hotone Audio Pulze Mini
> >   ALSA: usb-audio: Apply linear volume quirk on MV-SILICON devices
> 
> Thanks for patches!  The logic looks good and the implementation
> seems reasonable.  One slight concern was about the string matching
> patch (I explained in another mail), but others look fine.
> 

Thanks. I will send a v2 addressing that.

> Maybe the patch to add the missing documentation for
> QUIRK_FLAG_SKIP_IFACE_SETUP should be applied individually beforehand.
> Could you send it a fix for 7.0?
> 

Sure.

> The revert of volume range check makes sense, but maybe that should be
> combined with your rest changes.
> 

Yeah. Patch 1-3 as a whole targets 7.1, where patch 1 is also for
backporting (so that potential insane volume ranges can still trigger a
warning in LTS, which is better than nothing).

Thanks,
Rong

> 
> Takashi

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

* Re: [PATCH 4/9] ALSA: usb-audio: Support string-descriptor-based quirk table entry
  2026-03-01 21:37 ` [PATCH 4/9] ALSA: usb-audio: Support string-descriptor-based quirk table entry Rong Zhang
  2026-03-02  9:54   ` Takashi Iwai
@ 2026-03-05  6:13   ` Terry Junge
  2026-03-05 12:06     ` Takashi Iwai
  1 sibling, 1 reply; 17+ messages in thread
From: Terry Junge @ 2026-03-05  6:13 UTC (permalink / raw)
  To: Rong Zhang, Jaroslav Kysela, Takashi Iwai
  Cc: Jonathan Corbet, Shuah Khan, Cryolitia PukNgae, Arun Raghavan,
	linux-sound, linux-doc, linux-kernel, Icenowy Zheng



On 3/1/26 1:37 PM, Rong Zhang wrote:
> Some quirky devices do not have a unique VID/PID. Matching them using
> DEVICE_FLG() or VENDOR_FLG() may result in conflicts.
> 
> Add two new macros DEVICE_STRING_FLG() and VENDOR_STRING_FLG() to match
> USB string descriptors (manufacturer and/or product) in addition to VID
> and/or PID, so that we can deconflict these devices safely.
> 
> No functional change intended.
> 
> Signed-off-by: Rong Zhang <i@rong.moe>
> ---
>  sound/usb/quirks.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 82 insertions(+)
> 
> diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
> index c6a78efbcaa30..495dd46ce515c 100644
> --- a/sound/usb/quirks.c
> +++ b/sound/usb/quirks.c
> @@ -2,8 +2,10 @@
>  /*
>   */
>  
> +#include <linux/build_bug.h>
>  #include <linux/init.h>
>  #include <linux/slab.h>
> +#include <linux/string.h>
>  #include <linux/usb.h>
>  #include <linux/usb/audio.h>
>  #include <linux/usb/midi.h>
> @@ -2135,16 +2137,39 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
>  /*
>   * driver behavior quirk flags
>   */
> +struct usb_string_match {
> +	const char *manufacturer;
> +	const char *product;
> +};
> +
>  struct usb_audio_quirk_flags_table {
>  	u32 id;
>  	u32 flags;
> +	const struct usb_string_match *usb_string_match;
>  };
>  
>  #define DEVICE_FLG(vid, pid, _flags) \
>  	{ .id = USB_ID(vid, pid), .flags = (_flags) }
>  #define VENDOR_FLG(vid, _flags) DEVICE_FLG(vid, 0, _flags)
>  
> +/* Use as a last resort if using DEVICE_FLG() is prone to VID/PID conflicts. */
> +#define DEVICE_STRING_FLG(vid, pid, _manufacturer, _product, _flags)	\
> +{									\
> +	.id = USB_ID(vid, pid),						\
> +	.usb_string_match = &(const struct usb_string_match) {		\
> +		.manufacturer = _manufacturer,				\
> +		.product = _product,					\
> +	},								\
> +	.flags = (_flags),						\
> +}
> +
> +/* Use as a last resort if using VENDOR_FLG() is prone to VID conflicts. */
> +#define VENDOR_STRING_FLG(vid, _manufacturer, _flags)			\
> +	DEVICE_STRING_FLG(vid, 0, _manufacturer, NULL, _flags)
> +
>  static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
> +	/* Device and string descriptor matches */
> +
>  	/* Device matches */
>  	DEVICE_FLG(0x001f, 0x0b21, /* AB13X USB Audio */
>  		   QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
> @@ -2414,6 +2439,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
>  	DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */
>  		   QUIRK_FLAG_ALIGN_TRANSFER),
>  
> +	/* Vendor and string descriptor matches */
> +
>  	/* Vendor matches */
>  	VENDOR_FLG(0x045e, /* MS Lifecam */
>  		   QUIRK_FLAG_GET_SAMPLE_RATE),
> @@ -2558,14 +2585,69 @@ void snd_usb_apply_flag_dbg(const char *reason,
>  	}
>  }
>  
> +#define USB_STRING_SIZE 128
> +
> +static inline int snd_usb_get_strings(struct snd_usb_audio *chip,
> +				      char *manufacturer, char *product)

Hi Rong,

This function is not necessary.
Both the manufacturer and product strings can be found in struct usb_device.

If the device has a manufacturer string then chip->dev->manufacturer points to it.
Otherwise chip->dev->manufacturer is null.

Likewise, chip->dev->product points to the product string if there is one, else null.

> +{
> +	int ret;
> +
> +	if (manufacturer) {
> +		ret = usb_string(chip->dev, chip->dev->descriptor.iManufacturer,
> +				 manufacturer, USB_STRING_SIZE);
> +		if (ret < 0) {
> +			usb_audio_warn(chip, "failed to get manufacturer string: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	if (product) {
> +		ret = usb_string(chip->dev, chip->dev->descriptor.iProduct,
> +				 product, USB_STRING_SIZE);
> +		if (ret < 0) {
> +			usb_audio_warn(chip, "failed to get product string: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	return 1; /* ok */
> +}
> +
>  void snd_usb_init_quirk_flags_table(struct snd_usb_audio *chip)
>  {
>  	const struct usb_audio_quirk_flags_table *p;
> +	char manufacturer[USB_STRING_SIZE];
> +	char product[USB_STRING_SIZE];
> +	int got_usb_strings = 0; /* <0: error, 0: pending, >0: ok */
>  
>  	for (p = quirk_flags_table; p->id; p++) {
>  		if (chip->usb_id == p->id ||
>  		    (!USB_ID_PRODUCT(p->id) &&
>  		     USB_ID_VENDOR(chip->usb_id) == USB_ID_VENDOR(p->id))) {
> +			if (!p->usb_string_match)
> +				goto apply; /* DEVICE_FLG or VENDOR_FLG */
> +
> +			/* DEVICE_STRING_FLG or VENDOR_STRING_FLG */
> +			if (!got_usb_strings) {
> +				got_usb_strings = snd_usb_get_strings(chip,
> +					p->usb_string_match->manufacturer ? manufacturer : NULL,
> +					p->usb_string_match->product ? product : NULL);
> +			}

No need to get the strings as they can be found at chip->dev->manufacturer and chip->dev->product
if the device declared them.

> +
> +			BUILD_BUG_ON(!got_usb_strings);
> +
> +			if (got_usb_strings < 0)
> +				continue;
> +
> +			if (p->usb_string_match->manufacturer &&
> +			    strcmp(p->usb_string_match->manufacturer, manufacturer))
> +				continue;

Examples and intent:

DEVICE_STRING_FLG(vid, pid, "acme", "alpha", flags) // match vid, pid, "acme", and "alpha" strings
DEVICE_STRING_FLG(vid, pid, "acme", "", flags) // match vid, pid, "acme", and device has no product string
DEVICE_STRING_FLG(vid, pid, "acme", 0, flags) // match vid, pid, "acme", and any product string
DEVICE_STRING_FLG(vid, pid, 0, "alpha", flags) // match vid, pid, any manufacturer string, and "alpha"
DEVICE_STRING_FLG(vid, pid, "", "alpha", flags) // match vid, pid, no manufacturer string, and "alpha"
DEVICE_STRING_FLG(vid, pid, "", "", flags) // match vid, pid, no manufacturer, and no product strings

This test would change to something like

	if (p->usb_string_match->manufacturer &&
	    strcmp(p->usb_string_match->manufacturer, chip->dev->manufacturer ? chip->dev->manufacturer : ""))
		continue; 

> +
> +			if (p->usb_string_match->product &&
> +			    strcmp(p->usb_string_match->product, product))
> +				continue;

Same here but product instead of manufacturer.

Thanks,
Terry

> +
> +apply:
>  			snd_usb_apply_flag_dbg("builtin table", chip, p->flags);
>  			chip->quirk_flags |= p->flags;
>  			return;


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

* Re: [PATCH 4/9] ALSA: usb-audio: Support string-descriptor-based quirk table entry
  2026-03-05  6:13   ` Terry Junge
@ 2026-03-05 12:06     ` Takashi Iwai
  2026-03-05 12:24       ` Rong Zhang
  0 siblings, 1 reply; 17+ messages in thread
From: Takashi Iwai @ 2026-03-05 12:06 UTC (permalink / raw)
  To: Terry Junge
  Cc: Rong Zhang, Jaroslav Kysela, Takashi Iwai, Jonathan Corbet,
	Shuah Khan, Cryolitia PukNgae, Arun Raghavan, linux-sound,
	linux-doc, linux-kernel, Icenowy Zheng

On Thu, 05 Mar 2026 07:13:32 +0100,
Terry Junge wrote:
> 
> 
> 
> On 3/1/26 1:37 PM, Rong Zhang wrote:
> > Some quirky devices do not have a unique VID/PID. Matching them using
> > DEVICE_FLG() or VENDOR_FLG() may result in conflicts.
> > 
> > Add two new macros DEVICE_STRING_FLG() and VENDOR_STRING_FLG() to match
> > USB string descriptors (manufacturer and/or product) in addition to VID
> > and/or PID, so that we can deconflict these devices safely.
> > 
> > No functional change intended.
> > 
> > Signed-off-by: Rong Zhang <i@rong.moe>
> > ---
> >  sound/usb/quirks.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 82 insertions(+)
> > 
> > diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
> > index c6a78efbcaa30..495dd46ce515c 100644
> > --- a/sound/usb/quirks.c
> > +++ b/sound/usb/quirks.c
> > @@ -2,8 +2,10 @@
> >  /*
> >   */
> >  
> > +#include <linux/build_bug.h>
> >  #include <linux/init.h>
> >  #include <linux/slab.h>
> > +#include <linux/string.h>
> >  #include <linux/usb.h>
> >  #include <linux/usb/audio.h>
> >  #include <linux/usb/midi.h>
> > @@ -2135,16 +2137,39 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
> >  /*
> >   * driver behavior quirk flags
> >   */
> > +struct usb_string_match {
> > +	const char *manufacturer;
> > +	const char *product;
> > +};
> > +
> >  struct usb_audio_quirk_flags_table {
> >  	u32 id;
> >  	u32 flags;
> > +	const struct usb_string_match *usb_string_match;
> >  };
> >  
> >  #define DEVICE_FLG(vid, pid, _flags) \
> >  	{ .id = USB_ID(vid, pid), .flags = (_flags) }
> >  #define VENDOR_FLG(vid, _flags) DEVICE_FLG(vid, 0, _flags)
> >  
> > +/* Use as a last resort if using DEVICE_FLG() is prone to VID/PID conflicts. */
> > +#define DEVICE_STRING_FLG(vid, pid, _manufacturer, _product, _flags)	\
> > +{									\
> > +	.id = USB_ID(vid, pid),						\
> > +	.usb_string_match = &(const struct usb_string_match) {		\
> > +		.manufacturer = _manufacturer,				\
> > +		.product = _product,					\
> > +	},								\
> > +	.flags = (_flags),						\
> > +}
> > +
> > +/* Use as a last resort if using VENDOR_FLG() is prone to VID conflicts. */
> > +#define VENDOR_STRING_FLG(vid, _manufacturer, _flags)			\
> > +	DEVICE_STRING_FLG(vid, 0, _manufacturer, NULL, _flags)
> > +
> >  static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
> > +	/* Device and string descriptor matches */
> > +
> >  	/* Device matches */
> >  	DEVICE_FLG(0x001f, 0x0b21, /* AB13X USB Audio */
> >  		   QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
> > @@ -2414,6 +2439,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
> >  	DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */
> >  		   QUIRK_FLAG_ALIGN_TRANSFER),
> >  
> > +	/* Vendor and string descriptor matches */
> > +
> >  	/* Vendor matches */
> >  	VENDOR_FLG(0x045e, /* MS Lifecam */
> >  		   QUIRK_FLAG_GET_SAMPLE_RATE),
> > @@ -2558,14 +2585,69 @@ void snd_usb_apply_flag_dbg(const char *reason,
> >  	}
> >  }
> >  
> > +#define USB_STRING_SIZE 128
> > +
> > +static inline int snd_usb_get_strings(struct snd_usb_audio *chip,
> > +				      char *manufacturer, char *product)
> 
> Hi Rong,
> 
> This function is not necessary.
> Both the manufacturer and product strings can be found in struct usb_device.
> 
> If the device has a manufacturer string then chip->dev->manufacturer points to it.
> Otherwise chip->dev->manufacturer is null.
> 
> Likewise, chip->dev->product points to the product string if there is one, else null.
> 
> > +{
> > +	int ret;
> > +
> > +	if (manufacturer) {
> > +		ret = usb_string(chip->dev, chip->dev->descriptor.iManufacturer,
> > +				 manufacturer, USB_STRING_SIZE);
> > +		if (ret < 0) {
> > +			usb_audio_warn(chip, "failed to get manufacturer string: %d\n", ret);
> > +			return ret;
> > +		}
> > +	}
> > +
> > +	if (product) {
> > +		ret = usb_string(chip->dev, chip->dev->descriptor.iProduct,
> > +				 product, USB_STRING_SIZE);
> > +		if (ret < 0) {
> > +			usb_audio_warn(chip, "failed to get product string: %d\n", ret);
> > +			return ret;
> > +		}
> > +	}
> > +
> > +	return 1; /* ok */
> > +}
> > +
> >  void snd_usb_init_quirk_flags_table(struct snd_usb_audio *chip)
> >  {
> >  	const struct usb_audio_quirk_flags_table *p;
> > +	char manufacturer[USB_STRING_SIZE];
> > +	char product[USB_STRING_SIZE];
> > +	int got_usb_strings = 0; /* <0: error, 0: pending, >0: ok */
> >  
> >  	for (p = quirk_flags_table; p->id; p++) {
> >  		if (chip->usb_id == p->id ||
> >  		    (!USB_ID_PRODUCT(p->id) &&
> >  		     USB_ID_VENDOR(chip->usb_id) == USB_ID_VENDOR(p->id))) {
> > +			if (!p->usb_string_match)
> > +				goto apply; /* DEVICE_FLG or VENDOR_FLG */
> > +
> > +			/* DEVICE_STRING_FLG or VENDOR_STRING_FLG */
> > +			if (!got_usb_strings) {
> > +				got_usb_strings = snd_usb_get_strings(chip,
> > +					p->usb_string_match->manufacturer ? manufacturer : NULL,
> > +					p->usb_string_match->product ? product : NULL);
> > +			}
> 
> No need to get the strings as they can be found at chip->dev->manufacturer and chip->dev->product
> if the device declared them.
> 
> > +
> > +			BUILD_BUG_ON(!got_usb_strings);
> > +
> > +			if (got_usb_strings < 0)
> > +				continue;
> > +
> > +			if (p->usb_string_match->manufacturer &&
> > +			    strcmp(p->usb_string_match->manufacturer, manufacturer))
> > +				continue;
> 
> Examples and intent:
> 
> DEVICE_STRING_FLG(vid, pid, "acme", "alpha", flags) // match vid, pid, "acme", and "alpha" strings
> DEVICE_STRING_FLG(vid, pid, "acme", "", flags) // match vid, pid, "acme", and device has no product string
> DEVICE_STRING_FLG(vid, pid, "acme", 0, flags) // match vid, pid, "acme", and any product string
> DEVICE_STRING_FLG(vid, pid, 0, "alpha", flags) // match vid, pid, any manufacturer string, and "alpha"
> DEVICE_STRING_FLG(vid, pid, "", "alpha", flags) // match vid, pid, no manufacturer string, and "alpha"
> DEVICE_STRING_FLG(vid, pid, "", "", flags) // match vid, pid, no manufacturer, and no product strings
> 
> This test would change to something like
> 
> 	if (p->usb_string_match->manufacturer &&
> 	    strcmp(p->usb_string_match->manufacturer, chip->dev->manufacturer ? chip->dev->manufacturer : ""))
> 		continue; 
> 
> > +
> > +			if (p->usb_string_match->product &&
> > +			    strcmp(p->usb_string_match->product, product))
> > +				continue;
> 
> Same here but product instead of manufacturer.

Thanks, that's good to know!

Rong, could you submit the cleanup patch?  I already applied your
series, so write on top of sound.git tree for-next branch.

I'll work on a clean up of the other existing code in USB-audio, too.


Takashi

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

* Re: [PATCH 4/9] ALSA: usb-audio: Support string-descriptor-based quirk table entry
  2026-03-05 12:06     ` Takashi Iwai
@ 2026-03-05 12:24       ` Rong Zhang
  0 siblings, 0 replies; 17+ messages in thread
From: Rong Zhang @ 2026-03-05 12:24 UTC (permalink / raw)
  To: Takashi Iwai, Terry Junge
  Cc: Jaroslav Kysela, Takashi Iwai, Jonathan Corbet, Shuah Khan,
	Cryolitia PukNgae, Arun Raghavan, linux-sound, linux-doc,
	linux-kernel, Icenowy Zheng

Hi Takashi and Tery,

On Thu, 2026-03-05 at 13:06 +0100, Takashi Iwai wrote:
> On Thu, 05 Mar 2026 07:13:32 +0100,
> Terry Junge wrote:
> > 
> > 
> > 
> > On 3/1/26 1:37 PM, Rong Zhang wrote:
> > > Some quirky devices do not have a unique VID/PID. Matching them using
> > > DEVICE_FLG() or VENDOR_FLG() may result in conflicts.
> > > 
> > > Add two new macros DEVICE_STRING_FLG() and VENDOR_STRING_FLG() to match
> > > USB string descriptors (manufacturer and/or product) in addition to VID
> > > and/or PID, so that we can deconflict these devices safely.
> > > 
> > > No functional change intended.
> > > 
> > > Signed-off-by: Rong Zhang <i@rong.moe>
> > > ---
> > >  sound/usb/quirks.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 82 insertions(+)
> > > 
> > > diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
> > > index c6a78efbcaa30..495dd46ce515c 100644
> > > --- a/sound/usb/quirks.c
> > > +++ b/sound/usb/quirks.c
> > > @@ -2,8 +2,10 @@
> > >  /*
> > >   */
> > >  
> > > +#include <linux/build_bug.h>
> > >  #include <linux/init.h>
> > >  #include <linux/slab.h>
> > > +#include <linux/string.h>
> > >  #include <linux/usb.h>
> > >  #include <linux/usb/audio.h>
> > >  #include <linux/usb/midi.h>
> > > @@ -2135,16 +2137,39 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
> > >  /*
> > >   * driver behavior quirk flags
> > >   */
> > > +struct usb_string_match {
> > > +	const char *manufacturer;
> > > +	const char *product;
> > > +};
> > > +
> > >  struct usb_audio_quirk_flags_table {
> > >  	u32 id;
> > >  	u32 flags;
> > > +	const struct usb_string_match *usb_string_match;
> > >  };
> > >  
> > >  #define DEVICE_FLG(vid, pid, _flags) \
> > >  	{ .id = USB_ID(vid, pid), .flags = (_flags) }
> > >  #define VENDOR_FLG(vid, _flags) DEVICE_FLG(vid, 0, _flags)
> > >  
> > > +/* Use as a last resort if using DEVICE_FLG() is prone to VID/PID conflicts. */
> > > +#define DEVICE_STRING_FLG(vid, pid, _manufacturer, _product, _flags)	\
> > > +{									\
> > > +	.id = USB_ID(vid, pid),						\
> > > +	.usb_string_match = &(const struct usb_string_match) {		\
> > > +		.manufacturer = _manufacturer,				\
> > > +		.product = _product,					\
> > > +	},								\
> > > +	.flags = (_flags),						\
> > > +}
> > > +
> > > +/* Use as a last resort if using VENDOR_FLG() is prone to VID conflicts. */
> > > +#define VENDOR_STRING_FLG(vid, _manufacturer, _flags)			\
> > > +	DEVICE_STRING_FLG(vid, 0, _manufacturer, NULL, _flags)
> > > +
> > >  static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
> > > +	/* Device and string descriptor matches */
> > > +
> > >  	/* Device matches */
> > >  	DEVICE_FLG(0x001f, 0x0b21, /* AB13X USB Audio */
> > >  		   QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
> > > @@ -2414,6 +2439,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
> > >  	DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */
> > >  		   QUIRK_FLAG_ALIGN_TRANSFER),
> > >  
> > > +	/* Vendor and string descriptor matches */
> > > +
> > >  	/* Vendor matches */
> > >  	VENDOR_FLG(0x045e, /* MS Lifecam */
> > >  		   QUIRK_FLAG_GET_SAMPLE_RATE),
> > > @@ -2558,14 +2585,69 @@ void snd_usb_apply_flag_dbg(const char *reason,
> > >  	}
> > >  }
> > >  
> > > +#define USB_STRING_SIZE 128
> > > +
> > > +static inline int snd_usb_get_strings(struct snd_usb_audio *chip,
> > > +				      char *manufacturer, char *product)
> > 
> > Hi Rong,
> > 
> > This function is not necessary.
> > Both the manufacturer and product strings can be found in struct usb_device.
> > 
> > If the device has a manufacturer string then chip->dev->manufacturer points to it.
> > Otherwise chip->dev->manufacturer is null.
> > 
> > Likewise, chip->dev->product points to the product string if there is one, else null.
> > 
> > > +{
> > > +	int ret;
> > > +
> > > +	if (manufacturer) {
> > > +		ret = usb_string(chip->dev, chip->dev->descriptor.iManufacturer,
> > > +				 manufacturer, USB_STRING_SIZE);
> > > +		if (ret < 0) {
> > > +			usb_audio_warn(chip, "failed to get manufacturer string: %d\n", ret);
> > > +			return ret;
> > > +		}
> > > +	}
> > > +
> > > +	if (product) {
> > > +		ret = usb_string(chip->dev, chip->dev->descriptor.iProduct,
> > > +				 product, USB_STRING_SIZE);
> > > +		if (ret < 0) {
> > > +			usb_audio_warn(chip, "failed to get product string: %d\n", ret);
> > > +			return ret;
> > > +		}
> > > +	}
> > > +
> > > +	return 1; /* ok */
> > > +}
> > > +
> > >  void snd_usb_init_quirk_flags_table(struct snd_usb_audio *chip)
> > >  {
> > >  	const struct usb_audio_quirk_flags_table *p;
> > > +	char manufacturer[USB_STRING_SIZE];
> > > +	char product[USB_STRING_SIZE];
> > > +	int got_usb_strings = 0; /* <0: error, 0: pending, >0: ok */
> > >  
> > >  	for (p = quirk_flags_table; p->id; p++) {
> > >  		if (chip->usb_id == p->id ||
> > >  		    (!USB_ID_PRODUCT(p->id) &&
> > >  		     USB_ID_VENDOR(chip->usb_id) == USB_ID_VENDOR(p->id))) {
> > > +			if (!p->usb_string_match)
> > > +				goto apply; /* DEVICE_FLG or VENDOR_FLG */
> > > +
> > > +			/* DEVICE_STRING_FLG or VENDOR_STRING_FLG */
> > > +			if (!got_usb_strings) {
> > > +				got_usb_strings = snd_usb_get_strings(chip,
> > > +					p->usb_string_match->manufacturer ? manufacturer : NULL,
> > > +					p->usb_string_match->product ? product : NULL);
> > > +			}
> > 
> > No need to get the strings as they can be found at chip->dev->manufacturer and chip->dev->product
> > if the device declared them.
> > 
> > > +
> > > +			BUILD_BUG_ON(!got_usb_strings);
> > > +
> > > +			if (got_usb_strings < 0)
> > > +				continue;
> > > +
> > > +			if (p->usb_string_match->manufacturer &&
> > > +			    strcmp(p->usb_string_match->manufacturer, manufacturer))
> > > +				continue;
> > 
> > Examples and intent:
> > 
> > DEVICE_STRING_FLG(vid, pid, "acme", "alpha", flags) // match vid, pid, "acme", and "alpha" strings
> > DEVICE_STRING_FLG(vid, pid, "acme", "", flags) // match vid, pid, "acme", and device has no product string
> > DEVICE_STRING_FLG(vid, pid, "acme", 0, flags) // match vid, pid, "acme", and any product string
> > DEVICE_STRING_FLG(vid, pid, 0, "alpha", flags) // match vid, pid, any manufacturer string, and "alpha"
> > DEVICE_STRING_FLG(vid, pid, "", "alpha", flags) // match vid, pid, no manufacturer string, and "alpha"
> > DEVICE_STRING_FLG(vid, pid, "", "", flags) // match vid, pid, no manufacturer, and no product strings
> > 
> > This test would change to something like
> > 
> > 	if (p->usb_string_match->manufacturer &&
> > 	    strcmp(p->usb_string_match->manufacturer, chip->dev->manufacturer ? chip->dev->manufacturer : ""))
> > 		continue; 
> > 
> > > +
> > > +			if (p->usb_string_match->product &&
> > > +			    strcmp(p->usb_string_match->product, product))
> > > +				continue;
> > 
> > Same here but product instead of manufacturer.
> 
> Thanks, that's good to know!
> 

TIL too :-P

> Rong, could you submit the cleanup patch?  I already applied your
> series, so write on top of sound.git tree for-next branch.
> 

Sure. I think I would split it into a series of two patches, one
adopting chip->dev->{manufacturer,product} and one refining the
matching logic to allow matching "no string" since they are doing
different independent things.

Thanks,
Rong

> I'll work on a clean up of the other existing code in USB-audio, too.
> 
> 
> Takashi

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

end of thread, other threads:[~2026-03-05 12:29 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-01 21:37 [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID Rong Zhang
2026-03-01 21:37 ` [PATCH 1/9] Revert "ALSA: usb: Increase volume range that triggers a warning" Rong Zhang
2026-03-01 21:37 ` [PATCH 2/9] ALSA: usb-audio: Add helper function for volume range checks Rong Zhang
2026-03-01 21:37 ` [PATCH 3/9] ALSA: usb-audio: Improve " Rong Zhang
2026-03-01 21:37 ` [PATCH 4/9] ALSA: usb-audio: Support string-descriptor-based quirk table entry Rong Zhang
2026-03-02  9:54   ` Takashi Iwai
2026-03-02 12:31     ` Rong Zhang
2026-03-05  6:13   ` Terry Junge
2026-03-05 12:06     ` Takashi Iwai
2026-03-05 12:24       ` Rong Zhang
2026-03-01 21:37 ` [PATCH 5/9] ALSA: usb-audio: Deconflict VID between Focusrite Novation & MV-SILICON Rong Zhang
2026-03-01 21:37 ` [PATCH 6/9] ALSA: doc: Add doc for QUIRK_FLAG_SKIP_IFACE_SETUP of snd-usb-audio Rong Zhang
2026-03-01 21:37 ` [PATCH 7/9] ALSA: usb-audio: Add QUIRK_FLAG_MIXER_{PLAYBACK,CAPTURE}_LINEAR_VOL Rong Zhang
2026-03-01 21:37 ` [PATCH 8/9] ALSA: usb-audio: Add linear volume quirk for Hotone Audio Pulze Mini Rong Zhang
2026-03-01 21:37 ` [PATCH 9/9] ALSA: usb-audio: Apply linear volume quirk on MV-SILICON devices Rong Zhang
2026-03-02  9:59 ` [PATCH 0/9] ALSA: usb-audio: Add quirks for linear volume devices and deconflict VID Takashi Iwai
2026-03-02 12:40   ` Rong Zhang

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