[PATCH 0/4] ALSA: usb-audio: Option for delayed registration
Hi,
USB-audio driver probes per USB insterface, and this used to be the cause of the missing device enumeration on user-space because the driver registers the device per interface probe. The recently added quirk allows the delayed registration to sync with the last known stream, but we must have far more such devices.
This patch set adds a new option to let user specify the delayed registration device setup and also inform such devices at probe time.
Takashi
===
Takashi Iwai (4): ALSA: usb-audio: Rewrite registration quirk handling ALSA: usb-audio: Add delayed_register option ALSA: usb-audio: Inform devices that need delayed registration ALSA: usb-audio: Update the documentation for the new delayed_register option
Documentation/sound/alsa-configuration.rst | 13 ++++++++++ sound/usb/card.c | 28 ++++++++++++++++++++- sound/usb/quirks.c | 40 ++++++++++++++++++++++-------- sound/usb/quirks.h | 3 +-- sound/usb/stream.c | 3 +++ sound/usb/usbaudio.h | 1 + 6 files changed, 75 insertions(+), 13 deletions(-)
A slight refactoring of the registration quirk code. Now it uses the table lookup for easy additions in future. Also the return type was changed to bool, and got a few more comments.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/card.c | 2 +- sound/usb/quirks.c | 40 ++++++++++++++++++++++++++++++---------- sound/usb/quirks.h | 3 +-- 3 files changed, 32 insertions(+), 13 deletions(-)
diff --git a/sound/usb/card.c b/sound/usb/card.c index 16bbe2a50fb7..55d563a8154d 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -665,7 +665,7 @@ static int usb_audio_probe(struct usb_interface *intf, /* 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 */ - if (snd_usb_registration_quirk(chip, ifnum) == 0) { + if (!snd_usb_registration_quirk(chip, ifnum)) { err = snd_card_register(chip->card); if (err < 0) goto __error; diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index d605aff801b8..86f192a3043d 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1809,16 +1809,36 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip, } }
-int snd_usb_registration_quirk(struct snd_usb_audio *chip, - int iface) +/* + * 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 */ + { 0 } /* terminator */ +}; + +/* return true if skipping registration */ +bool snd_usb_registration_quirk(struct snd_usb_audio *chip, int iface) { - switch (chip->usb_id) { - case USB_ID(0x0951, 0x16d8): /* Kingston HyperX AMP */ - /* Register only when we reach interface 2 so that streams can - * merge correctly into PCMs from interface 0 - */ - return (iface != 2); - } + 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 0; + return false; } diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index 3afc01eabc7e..c76cf24a640a 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -51,7 +51,6 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip, struct audioformat *fp, int stream);
-int snd_usb_registration_quirk(struct snd_usb_audio *chip, - int iface); +bool snd_usb_registration_quirk(struct snd_usb_audio *chip, int iface);
#endif /* __USBAUDIO_QUIRKS_H */
Add a new option for specifying the quirk for delayed registration of the certain device. A list of devices can be passed in a form ID:IFACE,ID:IFACE,ID:IFACE,.... where ID is the 32bit hex number combo of vendor and device IDs and IFACE is the interface number to trigger the register.
When a matching device is probed, the card registration is delayed until the given interface is probed. It's needed for syncing the registration until the last interface when multiple interfaces are provided for the same card.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/card.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/sound/usb/card.c b/sound/usb/card.c index 55d563a8154d..951134238669 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -72,6 +72,7 @@ static int device_setup[SNDRV_CARDS]; /* device parameter for this card */ static bool ignore_ctl_error; static bool autoclock = true; static char *quirk_alias[SNDRV_CARDS]; +static char *delayed_register[SNDRV_CARDS];
bool snd_usb_use_vmalloc = true; bool snd_usb_skip_validation; @@ -95,6 +96,8 @@ module_param(autoclock, bool, 0444); MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes)."); module_param_array(quirk_alias, charp, NULL, 0444); MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef."); +module_param_array(delayed_register, charp, NULL, 0444); +MODULE_PARM_DESC(delayed_register, "Quirk for delayed registration, given by id:iface, e.g. 0123abcd:4."); module_param_named(use_vmalloc, snd_usb_use_vmalloc, bool, 0444); MODULE_PARM_DESC(use_vmalloc, "Use vmalloc for PCM intermediate buffers (default: yes)."); module_param_named(skip_validation, snd_usb_skip_validation, bool, 0444); @@ -525,6 +528,21 @@ 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) +{ + int i; + unsigned int id, inum; + + for (i = 0; i < ARRAY_SIZE(delayed_register); i++) { + if (delayed_register[i] && + sscanf(delayed_register[i], "%x:%x", &id, &inum) == 2 && + id == chip->usb_id) + return inum != iface; + } + + return false; +} + static const struct usb_device_id usb_audio_ids[]; /* defined below */
/* look for the corresponding quirk */ @@ -665,7 +683,8 @@ static int usb_audio_probe(struct usb_interface *intf, /* 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 */ - if (!snd_usb_registration_quirk(chip, ifnum)) { + if (!snd_usb_registration_quirk(chip, ifnum) && + !check_delayed_register_option(chip, ifnum)) { err = snd_card_register(chip->card); if (err < 0) goto __error;
The USB-audio driver may call snd_card_register() multiple times as its probe function is per USB interface while some USB-audio devices may provide multiple interfaces to assign different streams although they belong to the same device. This works in most cases but the registration is racy, hence it may miss the device recognition, e.g. PA doesn't see certain devices when hotplugged.
The recent addition of the delayed registration quirk allows to sync the registration at the last known interface, and the previous commit added a new module option to allow the dynamic setup for that purpose.
Now, this patch tries to find out and notifies for such devices that require the delayed registration. It shows a message like:
Found post-registration device assignment: 1234abcd:02
If you hit this message, you can pass delayed_register module option like:
snd_usb_audio.delayed_register=1234abcd:02
by just copying the last shown entry. If this works, it can be added statically in the quirk list, registration_quirks[] found at the end of sound/usb/quirks.c.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/usb/card.c | 7 +++++++ sound/usb/stream.c | 3 +++ sound/usb/usbaudio.h | 1 + 3 files changed, 11 insertions(+)
diff --git a/sound/usb/card.c b/sound/usb/card.c index 951134238669..fd6fd1726ea0 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -680,6 +680,13 @@ static int usb_audio_probe(struct usb_interface *intf, goto __error; }
+ if (chip->need_delayed_register) { + dev_info(&dev->dev, + "Found post-registration device assignment: %08x:%02x\n", + chip->usb_id, ifnum); + 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 */ diff --git a/sound/usb/stream.c b/sound/usb/stream.c index afd5aa574611..15296f2c902c 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -502,6 +502,9 @@ static int __snd_usb_add_audio_stream(struct snd_usb_audio *chip, subs = &as->substream[stream]; if (subs->ep_num) continue; + if (snd_device_get_state(chip->card, as->pcm) != + SNDRV_DEV_BUILD) + chip->need_delayed_register = true; err = snd_pcm_new_stream(as->pcm, stream, 1); if (err < 0) return err; diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 6fe3ab582ec6..1c892c7f14d7 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -34,6 +34,7 @@ struct snd_usb_audio { unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */ unsigned int tx_length_quirk:1; /* Put length specifier in transfers */ unsigned int setup_fmt_after_resume_quirk:1; /* setup the format to interface after resume */ + unsigned int need_delayed_register:1; /* warn for delayed registration */ int num_interfaces; int num_suspended_intf; int sample_rate_read_error;
Just adding a brief explanation to alsa-configuration.rst.
Signed-off-by: Takashi Iwai tiwai@suse.de --- Documentation/sound/alsa-configuration.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/Documentation/sound/alsa-configuration.rst b/Documentation/sound/alsa-configuration.rst index 392875a1b94e..72f97d4b01a7 100644 --- a/Documentation/sound/alsa-configuration.rst +++ b/Documentation/sound/alsa-configuration.rst @@ -2234,6 +2234,19 @@ use_vmalloc buffers. If mmap is used on such architectures, turn off this option, so that the DMA-coherent buffers are allocated and used instead. +delayed_register + The option is needed for devices that have multiple streams + defined in multiple USB interfaces. The driver may invoke + registrations multiple times (once per interface) and this may + lead to the insufficient device enumeration. + This option receives an array of strings, and you can pass + ID:INTERFACE like ``0123abcd:4`` for performing the delayed + registration to the given device. In this example, when a USB + device 0123:abcd is probed, the driver waits the registration + until the USB interface 4 gets probed. + The driver prints a message like "Found post-registration device + assignment: 1234abcd:04" for such a device, so that user can + notice the need.
This module supports multiple devices, autoprobe and hotplugging.
participants (1)
-
Takashi Iwai