diff options
author | Karol Kosik <k.kosik@outlook.com> | 2024-08-11 17:29:56 -0700 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2024-08-12 16:17:46 +0200 |
commit | 6aa8700150f7dc62f60b4cf5b1624e2e3d9ed78e (patch) | |
tree | 6885eeabc6b2ee31a111cce4ed64f14604401ca0 /sound/usb/stream.c | |
parent | ebfb5a57caa4e2e2203883ac6669bf8a294e7c89 (diff) |
ALSA: usb-audio: Support multiple control interfaces
Registering Numark Party Mix II fails with error 'bogus bTerminalLink 1'.
The problem stems from the driver not being able to find input/output
terminals required to configure audio streaming. The information about
those terminals is stored in AudioControl Interface. Numark device
contains 2 AudioControl Interfaces and the driver checks only one of them.
According to the USB standard, a device can have multiple audio functions,
each represented by Audio Interface Collection. Every audio function is
considered to be closed box and will contain unique AudioControl Interface
and zero or more AudioStreaming and MIDIStreaming Interfaces.
The Numark device adheres to the standard and defines two audio functions:
- MIDIStreaming function
- AudioStreaming function
It starts with MIDI function, followed by the audio function. The driver
saves the first AudioControl Interface in `snd_usb_audio` structure
associated with the entire device. It then attempts to use this interface
to query for terminals and clocks. However, this fails because the correct
information is stored in the second AudioControl Interface, defined in the
second Audio Interface Collection.
This patch introduces a structure holding association between each
MIDI/Audio Interface and its corresponding AudioControl Interface,
instead of relying on AudioControl Interface defined for the entire
device. This structure is populated during usb probing phase and leveraged
later when querying for terminals and when sending USB requests.
Alternative solutions considered include:
- defining a quirk for Numark where the order of interface is manually
changed, or terminals are hardcoded in the driver. This solution would
have fixed only this model, though it seems that device is USB compliant,
and it also seems that other devices from this company may be affected.
What's more, it looks like products from other manufacturers have similar
problems, i.e. Rane One DJ console
- keeping a list of all AudioControl Interfaces and querying all of them
to find required information. That would have solved my problem and have
low probability of breaking other devices, as we would always start with
the same logic of querying first AudioControl Interface. This solution
would not have followed the standard though.
This patch preserves the `snd_usb_audio.ctrl_intf` variable, which holds
the first AudioControl Interface, and uses it as a fallback when some
interfaces are not parsed correctly and lack an associated AudioControl
Interface, i.e., when configured via quirks.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=217865
Signed-off-by: Karol Kosik <k.kosik@outlook.com>
Link: https://patch.msgid.link/AS8P190MB1285893F4735C8B32AD3886BEC852@AS8P190MB1285.EURP190.PROD.OUTLOOK.COM
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/stream.c')
-rw-r--r-- | sound/usb/stream.c | 21 |
1 files changed, 13 insertions, 8 deletions
diff --git a/sound/usb/stream.c b/sound/usb/stream.c index e14c725acebf..d70c140813d6 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -713,10 +713,13 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, struct usb_device *dev = chip->dev; struct uac_format_type_i_continuous_descriptor *fmt; unsigned int num_channels = 0, chconfig = 0; + struct usb_host_interface *ctrl_intf; struct audioformat *fp; int clock = 0; u64 format; + ctrl_intf = snd_usb_find_ctrl_interface(chip, iface_no); + /* get audio formats */ if (protocol == UAC_VERSION_1) { struct uac1_as_header_descriptor *as = @@ -740,7 +743,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, format = le16_to_cpu(as->wFormatTag); /* remember the format value */ - iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, + iterm = snd_usb_find_input_terminal_descriptor(ctrl_intf, as->bTerminalLink, protocol); if (iterm) { @@ -776,7 +779,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, * lookup the terminal associated to this interface * to extract the clock */ - input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, + input_term = snd_usb_find_input_terminal_descriptor(ctrl_intf, as->bTerminalLink, protocol); if (input_term) { @@ -786,7 +789,7 @@ snd_usb_get_audioformat_uac12(struct snd_usb_audio *chip, goto found_clock; } - output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, + output_term = snd_usb_find_output_terminal_descriptor(ctrl_intf, as->bTerminalLink, protocol); if (output_term) { @@ -870,6 +873,7 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, struct uac3_cluster_header_descriptor *cluster; struct uac3_as_header_descriptor *as = NULL; struct uac3_hc_descriptor_header hc_header; + struct usb_host_interface *ctrl_intf; struct snd_pcm_chmap_elem *chmap; struct snd_usb_power_domain *pd; unsigned char badd_profile; @@ -881,6 +885,7 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, int err; badd_profile = chip->badd_profile; + ctrl_intf = snd_usb_find_ctrl_interface(chip, iface_no); if (badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { unsigned int maxpacksize = @@ -966,7 +971,7 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, cluster_id, - snd_usb_ctrl_intf(chip), + snd_usb_ctrl_intf(ctrl_intf), &hc_header, sizeof(hc_header)); if (err < 0) return ERR_PTR(err); @@ -990,7 +995,7 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, UAC3_CS_REQ_HIGH_CAPABILITY_DESCRIPTOR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, cluster_id, - snd_usb_ctrl_intf(chip), + snd_usb_ctrl_intf(ctrl_intf), cluster, wLength); if (err < 0) { kfree(cluster); @@ -1011,7 +1016,7 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, * lookup the terminal associated to this interface * to extract the clock */ - input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, + input_term = snd_usb_find_input_terminal_descriptor(ctrl_intf, as->bTerminalLink, UAC_VERSION_3); if (input_term) { @@ -1019,7 +1024,7 @@ snd_usb_get_audioformat_uac3(struct snd_usb_audio *chip, goto found_clock; } - output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf, + output_term = snd_usb_find_output_terminal_descriptor(ctrl_intf, as->bTerminalLink, UAC_VERSION_3); if (output_term) { @@ -1068,7 +1073,7 @@ found_clock: UAC_VERSION_3, iface_no); - pd = snd_usb_find_power_domain(chip->ctrl_intf, + pd = snd_usb_find_power_domain(ctrl_intf, as->bTerminalLink); /* ok, let's parse further... */ |