From: Cezary Rojewski <cezary.rojewski@intel.com>
To: broonie@kernel.org, tiwai@suse.com, perex@perex.cz
Cc: amadeuszx.slawinski@linux.intel.com, linux-sound@vger.kernel.org,
gregkh@linuxfoundation.org, quic_wcheng@quicinc.com,
mathias.nyman@linux.intel.com,
Cezary Rojewski <cezary.rojewski@intel.com>
Subject: [RFC 04/15] ALSA: usb: Implement two-stage quirk applying mechanism
Date: Wed, 9 Apr 2025 13:07:19 +0200 [thread overview]
Message-ID: <20250409110731.3752332-5-cezary.rojewski@intel.com> (raw)
In-Reply-To: <20250409110731.3752332-1-cezary.rojewski@intel.com>
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 --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,
--
2.25.1
next prev parent reply other threads:[~2025-04-09 10:51 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-04-09 11:07 [RFC 00/15] ALSA/ASoC: USB Audio Offload Cezary Rojewski
2025-04-09 11:07 ` [RFC 01/15] ALSA: usb: Move media-filters to the media code Cezary Rojewski
2025-04-09 11:07 ` [RFC 02/15] ALSA: usb: Drop private_free() usage for card and pcms Cezary Rojewski
2025-04-09 11:07 ` [RFC 03/15] ALSA: usb: Relocate the usbaudio header file Cezary Rojewski
2025-04-09 11:07 ` Cezary Rojewski [this message]
2025-04-09 11:07 ` [RFC 05/15] ALSA: usb: Implement two-stage stream creation mechanism Cezary Rojewski
2025-04-09 11:07 ` [RFC 06/15] ALSA: usb: Implement two-stage chip probing mechanism Cezary Rojewski
2025-04-09 11:07 ` [RFC 07/15] ALSA: usb: Switch to the two-stage chip probing Cezary Rojewski
2025-04-09 11:07 ` [RFC 08/15] ALSA: usb: Switch to the two-stage stream creation Cezary Rojewski
2025-04-09 11:07 ` [RFC 09/15] ALSA: usb: Switch to the two-stage quirk applying Cezary Rojewski
2025-04-09 11:07 ` [RFC 10/15] ALSA: usb: Export PCM operations Cezary Rojewski
2025-04-09 11:07 ` [RFC 11/15] ALSA: usb: Export usb_interface driver operations Cezary Rojewski
2025-04-09 11:07 ` [RFC 12/15] ALSA: usb: Export card-naming procedure Cezary Rojewski
2025-04-09 11:07 ` [RFC 13/15] ALSA: usb: Add getters to obtain endpoint information Cezary Rojewski
2025-04-09 11:07 ` [RFC 14/15] ASoC: codecs: Add USB-Audio driver Cezary Rojewski
2025-04-09 11:07 ` [RFC 15/15] ASoC: Intel: avs: Add USB machine board Cezary Rojewski
2025-04-09 12:10 ` [RFC 00/15] ALSA/ASoC: USB Audio Offload Greg KH
2025-04-09 13:06 ` Cezary Rojewski
2025-04-10 10:10 ` Takashi Iwai
2025-04-10 10:24 ` Takashi Iwai
2025-04-11 9:39 ` Cezary Rojewski
2025-04-15 16:15 ` Takashi Iwai
2025-04-17 10:15 ` Cezary Rojewski
2025-04-22 11:28 ` Pierre-Louis Bossart
2025-04-22 14:15 ` Cezary Rojewski
2025-04-25 16:53 ` Pierre-Louis Bossart
2025-04-11 14:04 ` Greg KH
2025-04-11 16:51 ` Cezary Rojewski
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250409110731.3752332-5-cezary.rojewski@intel.com \
--to=cezary.rojewski@intel.com \
--cc=amadeuszx.slawinski@linux.intel.com \
--cc=broonie@kernel.org \
--cc=gregkh@linuxfoundation.org \
--cc=linux-sound@vger.kernel.org \
--cc=mathias.nyman@linux.intel.com \
--cc=perex@perex.cz \
--cc=quic_wcheng@quicinc.com \
--cc=tiwai@suse.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox