ALSA: usb-audio: Register card at the last interface
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>
This commit is contained in:
parent
16c5cde20e
commit
6392dcd1d0
@ -690,7 +690,7 @@ static bool get_alias_id(struct usb_device *dev, unsigned int *id)
|
|||||||
return false;
|
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;
|
int i;
|
||||||
unsigned int id, inum;
|
unsigned int id, inum;
|
||||||
@ -699,14 +699,31 @@ static bool check_delayed_register_option(struct snd_usb_audio *chip, int iface)
|
|||||||
if (delayed_register[i] &&
|
if (delayed_register[i] &&
|
||||||
sscanf(delayed_register[i], "%x:%x", &id, &inum) == 2 &&
|
sscanf(delayed_register[i], "%x:%x", &id, &inum) == 2 &&
|
||||||
id == chip->usb_id)
|
id == chip->usb_id)
|
||||||
return iface < inum;
|
return inum;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct usb_device_id usb_audio_ids[]; /* defined below */
|
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 */
|
/* look for the corresponding quirk */
|
||||||
static const struct snd_usb_audio_quirk *
|
static const struct snd_usb_audio_quirk *
|
||||||
get_alias_quirk(struct usb_device *dev, unsigned int id)
|
get_alias_quirk(struct usb_device *dev, unsigned int id)
|
||||||
@ -813,6 +830,7 @@ static int usb_audio_probe(struct usb_interface *intf,
|
|||||||
err = -ENODEV;
|
err = -ENODEV;
|
||||||
goto __error;
|
goto __error;
|
||||||
}
|
}
|
||||||
|
find_last_interface(chip);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chip->num_interfaces >= MAX_CARD_INTERFACES) {
|
if (chip->num_interfaces >= MAX_CARD_INTERFACES) {
|
||||||
@ -862,11 +880,11 @@ static int usb_audio_probe(struct usb_interface *intf,
|
|||||||
chip->need_delayed_register = false; /* clear again */
|
chip->need_delayed_register = false; /* clear again */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we are allowed to call snd_card_register() many times, but first
|
/* register card if we reach to the last interface or to the specified
|
||||||
* check to see if a device needs to skip it or do anything special
|
* one given via option
|
||||||
*/
|
*/
|
||||||
if (!snd_usb_registration_quirk(chip, ifnum) &&
|
if (check_delayed_register_option(chip) == ifnum ||
|
||||||
!check_delayed_register_option(chip, ifnum)) {
|
chip->last_iface == ifnum) {
|
||||||
err = snd_card_register(chip->card);
|
err = snd_card_register(chip->card);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto __error;
|
goto __error;
|
||||||
|
@ -2030,48 +2030,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
|
* driver behavior quirk flags
|
||||||
*/
|
*/
|
||||||
|
@ -48,8 +48,6 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
|
|||||||
struct audioformat *fp,
|
struct audioformat *fp,
|
||||||
int stream);
|
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);
|
void snd_usb_init_quirk_flags(struct snd_usb_audio *chip);
|
||||||
|
|
||||||
#endif /* __USBAUDIO_QUIRKS_H */
|
#endif /* __USBAUDIO_QUIRKS_H */
|
||||||
|
@ -37,6 +37,7 @@ struct snd_usb_audio {
|
|||||||
unsigned int quirk_flags;
|
unsigned int quirk_flags;
|
||||||
unsigned int need_delayed_register:1; /* warn for delayed registration */
|
unsigned int need_delayed_register:1; /* warn for delayed registration */
|
||||||
int num_interfaces;
|
int num_interfaces;
|
||||||
|
int last_iface;
|
||||||
int num_suspended_intf;
|
int num_suspended_intf;
|
||||||
int sample_rate_read_error;
|
int sample_rate_read_error;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user