ALSA: usb-audio: Register card at the last interface
authorTakashi Iwai <tiwai@suse.de>
Sun, 4 Sep 2022 16:12:47 +0000 (18:12 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 26 Oct 2022 10:35:39 +0000 (12:35 +0200)
[ Upstream commit 6392dcd1d0c7034ccf630ec55fc9e5810ecadf3b ]

The USB-audio driver matches per interface, and as default, it
registers the card instance at the very first instance.  This can be a
problem for the devices that have multiple interfaces to be probed, as
the udev rule isn't applied properly for the later appearing
interfaces.  Although we introduced the delayed_register option and
the quirks for covering those shortcomings, it's nothing but a
workaround for specific devices.

This patch is an another attempt to fix the problem in a more generic
way.  Now the driver checks the whole USB device descriptor at the
very first time when an interface is attached to a sound card.  It
looks at each matching interface in the descriptor and remembers the
last matching one.  The snd_card_register() is invoked only when this
last interface is probed.

After this change, the quirks for the delayed registration become
superfluous, hence they are removed along with the patch.  OTOH, the
delayed_register option is still kept, as it might be useful for some
corner cases (e.g. a special driver overtakes the interface probe from
the standard driver, and the last interface probe may miss).

Link: https://lore.kernel.org/r/20220904161247.16461-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Sasha Levin <sashal@kernel.org>
sound/usb/card.c
sound/usb/quirks.c
sound/usb/quirks.h
sound/usb/usbaudio.h

index 713b84d..207c212 100644 (file)
@@ -689,7 +689,7 @@ static bool get_alias_id(struct usb_device *dev, unsigned int *id)
        return false;
 }
 
-static bool check_delayed_register_option(struct snd_usb_audio *chip, int iface)
+static int check_delayed_register_option(struct snd_usb_audio *chip)
 {
        int i;
        unsigned int id, inum;
@@ -698,14 +698,31 @@ static bool check_delayed_register_option(struct snd_usb_audio *chip, int iface)
                if (delayed_register[i] &&
                    sscanf(delayed_register[i], "%x:%x", &id, &inum) == 2 &&
                    id == chip->usb_id)
-                       return iface < inum;
+                       return inum;
        }
 
-       return false;
+       return -1;
 }
 
 static const struct usb_device_id usb_audio_ids[]; /* defined below */
 
+/* look for the last interface that matches with our ids and remember it */
+static void find_last_interface(struct snd_usb_audio *chip)
+{
+       struct usb_host_config *config = chip->dev->actconfig;
+       struct usb_interface *intf;
+       int i;
+
+       if (!config)
+               return;
+       for (i = 0; i < config->desc.bNumInterfaces; i++) {
+               intf = config->interface[i];
+               if (usb_match_id(intf, usb_audio_ids))
+                       chip->last_iface = intf->altsetting[0].desc.bInterfaceNumber;
+       }
+       usb_audio_dbg(chip, "Found last interface = %d\n", chip->last_iface);
+}
+
 /* look for the corresponding quirk */
 static const struct snd_usb_audio_quirk *
 get_alias_quirk(struct usb_device *dev, unsigned int id)
@@ -812,6 +829,7 @@ static int usb_audio_probe(struct usb_interface *intf,
                        err = -ENODEV;
                        goto __error;
                }
+               find_last_interface(chip);
        }
 
        if (chip->num_interfaces >= MAX_CARD_INTERFACES) {
@@ -861,11 +879,11 @@ static int usb_audio_probe(struct usb_interface *intf,
                chip->need_delayed_register = false; /* clear again */
        }
 
-       /* we are allowed to call snd_card_register() many times, but first
-        * check to see if a device needs to skip it or do anything special
+       /* register card if we reach to the last interface or to the specified
+        * one given via option
         */
-       if (!snd_usb_registration_quirk(chip, ifnum) &&
-           !check_delayed_register_option(chip, ifnum)) {
+       if (check_delayed_register_option(chip) == ifnum ||
+           chip->last_iface == ifnum) {
                err = snd_card_register(chip->card);
                if (err < 0)
                        goto __error;
index 5b4d8f5..8c3b0be 100644 (file)
@@ -1729,48 +1729,6 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
 }
 
 /*
- * registration quirk:
- * the registration is skipped if a device matches with the given ID,
- * unless the interface reaches to the defined one.  This is for delaying
- * the registration until the last known interface, so that the card and
- * devices appear at the same time.
- */
-
-struct registration_quirk {
-       unsigned int usb_id;    /* composed via USB_ID() */
-       unsigned int interface; /* the interface to trigger register */
-};
-
-#define REG_QUIRK_ENTRY(vendor, product, iface) \
-       { .usb_id = USB_ID(vendor, product), .interface = (iface) }
-
-static const struct registration_quirk registration_quirks[] = {
-       REG_QUIRK_ENTRY(0x0951, 0x16d8, 2),     /* Kingston HyperX AMP */
-       REG_QUIRK_ENTRY(0x0951, 0x16ed, 2),     /* Kingston HyperX Cloud Alpha S */
-       REG_QUIRK_ENTRY(0x0951, 0x16ea, 2),     /* Kingston HyperX Cloud Flight S */
-       REG_QUIRK_ENTRY(0x0ecb, 0x1f46, 2),     /* JBL Quantum 600 */
-       REG_QUIRK_ENTRY(0x0ecb, 0x1f47, 2),     /* JBL Quantum 800 */
-       REG_QUIRK_ENTRY(0x0ecb, 0x1f4c, 2),     /* JBL Quantum 400 */
-       REG_QUIRK_ENTRY(0x0ecb, 0x2039, 2),     /* JBL Quantum 400 */
-       REG_QUIRK_ENTRY(0x0ecb, 0x203c, 2),     /* JBL Quantum 600 */
-       REG_QUIRK_ENTRY(0x0ecb, 0x203e, 2),     /* JBL Quantum 800 */
-       { 0 }                                   /* terminator */
-};
-
-/* return true if skipping registration */
-bool snd_usb_registration_quirk(struct snd_usb_audio *chip, int iface)
-{
-       const struct registration_quirk *q;
-
-       for (q = registration_quirks; q->usb_id; q++)
-               if (chip->usb_id == q->usb_id)
-                       return iface < q->interface;
-
-       /* Register as normal */
-       return false;
-}
-
-/*
  * driver behavior quirk flags
  */
 struct usb_audio_quirk_flags_table {
index 31abb7c..f9bfd5a 100644 (file)
@@ -48,8 +48,6 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
                                          struct audioformat *fp,
                                          int stream);
 
-bool snd_usb_registration_quirk(struct snd_usb_audio *chip, int iface);
-
 void snd_usb_init_quirk_flags(struct snd_usb_audio *chip);
 
 #endif /* __USBAUDIO_QUIRKS_H */
index 044cd7a..39c3c61 100644 (file)
@@ -37,6 +37,7 @@ struct snd_usb_audio {
        unsigned int quirk_flags;
        unsigned int need_delayed_register:1; /* warn for delayed registration */
        int num_interfaces;
+       int last_iface;
        int num_suspended_intf;
        int sample_rate_read_error;