diff mbox series

[RFC,04/15] ALSA: usb: Implement two-stage quirk applying mechanism

Message ID 20250409110731.3752332-5-cezary.rojewski@intel.com (mailing list archive)
State RFC
Headers show
Series ALSA/ASoC: USB Audio Offload | expand

Commit Message

Cezary Rojewski April 9, 2025, 11:07 a.m. UTC
The USB Audio-Class (AC) device driver on ASoC side needs to know the
number of available PCM streams when creating list of DAI and DAI Links
before these objects are actually probed. To achieve that split existing
USB stream enumeration procedure from one-stage to two-stage process:

1) parse the descriptors and obtain information about PCMs but do not
   create them yet
2) create all PCMs based on the previously obtained information and
   follow that up with MIDIs, mixers and Media resources

On quirks side this translates to splitting all handlers that do combine
pcm/midi/mixer together into 'pcm-only' and 'others'. A top level
handler snd_usb_parse_pcms_quirk() is provided to invoke all the
necessary parse-pcm quirks depending on the quirk type. It mimics
behavior of snd_usb_create_quirk() but limits itself to just parsing the
PCMs.

Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
---
 sound/usb/quirks.c | 217 ++++++++++++++++++++++++++++++++++++---------
 sound/usb/quirks.h |   4 +
 2 files changed, 179 insertions(+), 42 deletions(-)
diff mbox series

Patch

diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 2a9470ef8b5f..4e402c6406a9 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -28,7 +28,36 @@ 
 #include "stream.h"
 
 /*
- * handle the quirks for the contained interfaces
+ * First run of composite quirk. Parses all PCM interfaces.
+ * These can be later created with snd_usb_add_pcms().
+ */
+static int create_composite_pcm_quirk(struct snd_usb_audio *chip,
+				      struct usb_interface *iface,
+				      struct usb_driver *driver,
+				      const struct snd_usb_audio_quirk *quirk_comp)
+{
+	int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber;
+	const struct snd_usb_audio_quirk *quirk;
+	int err;
+
+	for (quirk = quirk_comp->data; quirk->ifnum >= 0; ++quirk) {
+		iface = usb_ifnum_to_if(chip->dev, quirk->ifnum);
+		if (!iface)
+			continue;
+		if (quirk->ifnum != probed_ifnum &&
+		    usb_interface_claimed(iface))
+			continue;
+		err = snd_usb_parse_pcms_quirk(chip, iface, driver, quirk);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/*
+ * Second run of composite quirk. Creates all non-PCM interfaces and
+ * claims the usb_interface @iface.
  */
 static int create_composite_quirk(struct snd_usb_audio *chip,
 				  struct usb_interface *iface,
@@ -375,6 +404,25 @@  static int create_auto_midi_quirk(struct snd_usb_audio *chip,
 	return create_std_midi_quirk(chip, iface, driver, alts);
 }
 
+static int create_autodetect_pcm_quirk(struct snd_usb_audio *chip,
+				       struct usb_interface *iface,
+				       struct usb_driver *driver,
+				       const struct snd_usb_audio_quirk *quirk)
+{
+	return create_auto_pcm_quirk(chip, iface, driver);
+}
+
+__maybe_unused
+static int create_autodetect_midi_quirk(struct snd_usb_audio *chip,
+					struct usb_interface *iface,
+					struct usb_driver *driver,
+					const struct snd_usb_audio_quirk *quirk)
+{
+	if (list_empty(&chip->pcm_list))
+		return create_auto_midi_quirk(chip, iface, driver);
+	return 1;
+}
+
 static int create_autodetect_quirk(struct snd_usb_audio *chip,
 				   struct usb_interface *iface,
 				   struct usb_driver *driver,
@@ -388,14 +436,10 @@  static int create_autodetect_quirk(struct snd_usb_audio *chip,
 	return err;
 }
 
-/*
- * Create a stream for an Edirol UA-700/UA-25/UA-4FX interface.  
- * The only way to detect the sample rate is by looking at wMaxPacketSize.
- */
-static int create_uaxx_quirk(struct snd_usb_audio *chip,
-			     struct usb_interface *iface,
-			     struct usb_driver *driver,
-			     const struct snd_usb_audio_quirk *quirk)
+static int create_uaxx_pcm_quirk(struct snd_usb_audio *chip,
+				 struct usb_interface *iface,
+				 struct usb_driver *driver,
+				 const struct snd_usb_audio_quirk *quirk)
 {
 	static const struct audioformat ua_format = {
 		.formats = SNDRV_PCM_FMTBIT_S24_3LE,
@@ -405,8 +449,8 @@  static int create_uaxx_quirk(struct snd_usb_audio *chip,
 		.altset_idx = 1,
 		.rates = SNDRV_PCM_RATE_CONTINUOUS,
 	};
-	struct usb_host_interface *alts;
 	struct usb_interface_descriptor *altsd;
+	struct usb_host_interface *alts;
 	struct audioformat *fp;
 	int err;
 
@@ -416,35 +460,15 @@  static int create_uaxx_quirk(struct snd_usb_audio *chip,
 	alts = &iface->altsetting[1];
 	altsd = get_iface_desc(alts);
 
-	if (altsd->bNumEndpoints == 2) {
-		static const struct snd_usb_midi_endpoint_info ua700_ep = {
-			.out_cables = 0x0003,
-			.in_cables  = 0x0003
-		};
-		static const struct snd_usb_audio_quirk ua700_quirk = {
-			.type = QUIRK_MIDI_FIXED_ENDPOINT,
-			.data = &ua700_ep
-		};
-		static const struct snd_usb_midi_endpoint_info uaxx_ep = {
-			.out_cables = 0x0001,
-			.in_cables  = 0x0001
-		};
-		static const struct snd_usb_audio_quirk uaxx_quirk = {
-			.type = QUIRK_MIDI_FIXED_ENDPOINT,
-			.data = &uaxx_ep
-		};
-		const struct snd_usb_audio_quirk *quirk =
-			chip->usb_id == USB_ID(0x0582, 0x002b)
-			? &ua700_quirk : &uaxx_quirk;
-		return __snd_usbmidi_create(chip->card, iface,
-					    &chip->midi_list, quirk,
-					    chip->usb_id,
-					    &chip->num_rawmidis);
+	switch (altsd->bNumEndpoints) {
+	case 2:
+		return 2; /* Leave this to midi. */
+	case 1:
+		break;
+	default:
+		return -ENXIO;
 	}
 
-	if (altsd->bNumEndpoints != 1)
-		return -ENXIO;
-
 	fp = kmemdup(&ua_format, sizeof(*fp), GFP_KERNEL);
 	if (!fp)
 		return -ENOMEM;
@@ -484,6 +508,78 @@  static int create_uaxx_quirk(struct snd_usb_audio *chip,
 	return 0;
 }
 
+/*
+ * Create a stream for an Edirol UA-700/UA-25/UA-4FX interface.
+ * The only way to detect the sample rate is by looking at wMaxPacketSize.
+ */
+static int create_uaxx_midi_quirk(struct snd_usb_audio *chip,
+				  struct usb_interface *iface,
+				  struct usb_driver *driver,
+				  const struct snd_usb_audio_quirk *quirk)
+{
+	static const struct snd_usb_midi_endpoint_info ua700_ep = {
+		.out_cables = 0x0003,
+		.in_cables  = 0x0003
+	};
+	static const struct snd_usb_audio_quirk ua700_quirk = {
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = &ua700_ep
+	};
+	static const struct snd_usb_midi_endpoint_info uaxx_ep = {
+		.out_cables = 0x0001,
+		.in_cables  = 0x0001
+	};
+	static const struct snd_usb_audio_quirk uaxx_quirk = {
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = &uaxx_ep
+	};
+	const struct snd_usb_audio_quirk *spec;
+	struct usb_host_interface *alt;
+
+	spec = chip->usb_id == USB_ID(0x0582, 0x002b) ? &ua700_quirk : &uaxx_quirk;
+
+	/* both PCM and MIDI interfaces have 2 or more altsettings */
+	if (iface->num_altsetting < 2)
+		return -ENXIO;
+	alt = &iface->altsetting[1];
+
+	switch (alt->desc.bNumEndpoints) {
+	case 2:
+		break;
+	case 1:
+		return 1; /* Leave this to pcm. */
+	default:
+		return -ENXIO;
+	}
+
+	return __snd_usbmidi_create(chip->card, iface, &chip->midi_list, spec, chip->usb_id,
+				    &chip->num_rawmidis);
+}
+
+static int create_uaxx_quirk(struct snd_usb_audio *chip,
+			     struct usb_interface *iface,
+			     struct usb_driver *driver,
+			     const struct snd_usb_audio_quirk *quirk)
+{
+	struct usb_interface_descriptor *altsd;
+	struct usb_host_interface *alts;
+
+	/* both PCM and MIDI interfaces have 2 or more altsettings */
+	if (iface->num_altsetting < 2)
+		return -ENXIO;
+	alts = &iface->altsetting[1];
+	altsd = get_iface_desc(alts);
+
+	switch (altsd->bNumEndpoints) {
+	case 2:
+		return create_uaxx_midi_quirk(chip, iface, driver, quirk);
+	case 1:
+		return create_uaxx_pcm_quirk(chip, iface, driver, quirk);
+	default:
+		return -ENXIO;
+	}
+}
+
 /*
  * Create a standard mixer for the specified interface.
  */
@@ -498,6 +594,44 @@  static int create_standard_mixer_quirk(struct snd_usb_audio *chip,
 	return snd_usb_create_mixer(chip, quirk->ifnum);
 }
 
+int snd_usb_parse_pcms_quirk(struct snd_usb_audio *chip,
+			     struct usb_interface *iface,
+			     struct usb_driver *driver,
+			     const struct snd_usb_audio_quirk *quirk)
+{
+	typedef int (*quirk_func_t)(struct snd_usb_audio *,
+				    struct usb_interface *,
+				    struct usb_driver *,
+				    const struct snd_usb_audio_quirk *);
+	static const quirk_func_t quirk_funcs[] = {
+		[QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk,
+		[QUIRK_COMPOSITE] = create_composite_pcm_quirk,
+		[QUIRK_AUTODETECT] = create_autodetect_pcm_quirk,
+		[QUIRK_MIDI_STANDARD_INTERFACE] = ignore_interface_quirk,
+		[QUIRK_MIDI_FIXED_ENDPOINT] = ignore_interface_quirk,
+		[QUIRK_MIDI_YAMAHA] = ignore_interface_quirk,
+		[QUIRK_MIDI_ROLAND] = ignore_interface_quirk,
+		[QUIRK_MIDI_MIDIMAN] = ignore_interface_quirk,
+		[QUIRK_MIDI_NOVATION] = ignore_interface_quirk,
+		[QUIRK_MIDI_RAW_BYTES] = ignore_interface_quirk,
+		[QUIRK_MIDI_EMAGIC] = ignore_interface_quirk,
+		[QUIRK_MIDI_CME] = ignore_interface_quirk,
+		[QUIRK_MIDI_AKAI] = ignore_interface_quirk,
+		[QUIRK_MIDI_FTDI] = ignore_interface_quirk,
+		[QUIRK_MIDI_CH345] = ignore_interface_quirk,
+		[QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
+		[QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
+		[QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_pcm_quirk,
+		[QUIRK_AUDIO_STANDARD_MIXER] = ignore_interface_quirk,
+	};
+
+	if (quirk->type < QUIRK_TYPE_COUNT)
+		return quirk_funcs[quirk->type](chip, iface, driver, quirk);
+
+	usb_audio_err(chip, "invalid quirk type %d\n", quirk->type);
+	return -ENXIO;
+}
+
 /*
  * audio-interface quirks
  *
@@ -537,12 +671,11 @@  int snd_usb_create_quirk(struct snd_usb_audio *chip,
 		[QUIRK_AUDIO_STANDARD_MIXER] = create_standard_mixer_quirk,
 	};
 
-	if (quirk->type < QUIRK_TYPE_COUNT) {
+	if (quirk->type < QUIRK_TYPE_COUNT)
 		return quirk_funcs[quirk->type](chip, iface, driver, quirk);
-	} else {
-		usb_audio_err(chip, "invalid quirk type %d\n", quirk->type);
-		return -ENXIO;
-	}
+
+	usb_audio_err(chip, "invalid quirk type %d\n", quirk->type);
+	return -ENXIO;
 }
 
 /*
diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h
index 0ea079688261..1344e4e635d8 100644
--- a/sound/usb/quirks.h
+++ b/sound/usb/quirks.h
@@ -8,6 +8,10 @@  struct audioformat;
 struct snd_usb_endpoint;
 struct snd_usb_substream;
 
+int snd_usb_parse_pcms_quirk(struct snd_usb_audio *chip,
+			     struct usb_interface *iface,
+			     struct usb_driver *driver,
+			     const struct snd_usb_audio_quirk *quirk);
 int snd_usb_create_quirk(struct snd_usb_audio *chip,
 			 struct usb_interface *iface,
 			 struct usb_driver *driver,