diff mbox series

[04/41] ALSA: usb-audio: Check implicit feedback EP generically for UAC2

Message ID 20201123085347.19667-5-tiwai@suse.de (mailing list archive)
State New, archived
Headers show
Series USB audio refactoring for better implicit feedback support | expand

Commit Message

Takashi Iwai Nov. 23, 2020, 8:53 a.m. UTC
It seems that many UAC2 devices are with the implicit feedback, but
they couldn't be probed properly because the assumption the driver
takes currently isn't applied: they have the single endpoint for both
data and implicit-fb streams, while we checked only the classical sync
endpoints assigned to the next altsetting in the same interface.

This patch extends the search to match with those typical cases where
the implicit fb stream is found in the next interface number.

While we're at it, slightly refactor the code, not returning 0/-ERROR
but use the standard bool to success/failur, which is more intuitive
in this particular case.

Reported-by: Dylan Robinson <dylan_robinson@motu.com>
Tested-by: Keith Milner <kamilner@superlative.org>
Tested-by: Dylan Robinson <dylan_robinson@motu.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/usb/pcm.c | 83 +++++++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 66 insertions(+), 17 deletions(-)
diff mbox series

Patch

diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 380d7275d187..2b11c2c837bf 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -272,33 +272,70 @@  static int snd_usb_pcm_sync_stop(struct snd_pcm_substream *substream)
 	return 0;
 }
 
-static int search_roland_implicit_fb(struct usb_device *dev, int ifnum,
-				     unsigned int altsetting,
-				     struct usb_host_interface **alts,
-				     unsigned int *ep)
+/* Check whether the given iface:altsetting points to an implicit fb source */
+static bool search_generic_implicit_fb(struct usb_device *dev, int ifnum,
+				       unsigned int altsetting,
+				       struct usb_host_interface **altsp,
+				       unsigned int *ep)
 {
 	struct usb_interface *iface;
+	struct usb_host_interface *alts;
+	struct usb_interface_descriptor *altsd;
+	struct usb_endpoint_descriptor *epd;
+
+	iface = usb_ifnum_to_if(dev, ifnum);
+	if (!iface)
+		return false;
+	alts = usb_altnum_to_altsetting(iface, altsetting);
+	if (!alts)
+		return false;
+	altsd = get_iface_desc(alts);
+	if (altsd->bInterfaceClass != USB_CLASS_AUDIO ||
+	    altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING ||
+	    altsd->bInterfaceProtocol != UAC_VERSION_2 ||
+	    altsd->bNumEndpoints < 1)
+		return false;
+	epd = get_endpoint(alts, 0);
+	if (!usb_endpoint_is_isoc_in(epd) ||
+	    (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) !=
+					USB_ENDPOINT_USAGE_IMPLICIT_FB)
+		return false;
+	*ep = epd->bEndpointAddress;
+	*altsp = alts;
+	return true;
+}
+
+/* Like the function above, but specific to Roland with vendor class and hack */
+static bool search_roland_implicit_fb(struct usb_device *dev, int ifnum,
+				      unsigned int altsetting,
+				      struct usb_host_interface **altsp,
+				      unsigned int *ep)
+{
+	struct usb_interface *iface;
+	struct usb_host_interface *alts;
 	struct usb_interface_descriptor *altsd;
 	struct usb_endpoint_descriptor *epd;
 
 	iface = usb_ifnum_to_if(dev, ifnum);
-	if (!iface || iface->num_altsetting < altsetting + 1)
-		return -ENOENT;
-	*alts = &iface->altsetting[altsetting];
-	altsd = get_iface_desc(*alts);
-	if (altsd->bAlternateSetting != altsetting ||
-	    altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC ||
+	if (!iface)
+		return false;
+	alts = usb_altnum_to_altsetting(iface, altsetting);
+	if (!alts)
+		return false;
+	altsd = get_iface_desc(alts);
+	if (altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC ||
 	    (altsd->bInterfaceSubClass != 2 &&
-	     altsd->bInterfaceProtocol != 2   ) ||
+	     altsd->bInterfaceProtocol != 2) ||
 	    altsd->bNumEndpoints < 1)
-		return -ENOENT;
-	epd = get_endpoint(*alts, 0);
+		return false;
+	epd = get_endpoint(alts, 0);
 	if (!usb_endpoint_is_isoc_in(epd) ||
 	    (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) !=
 					USB_ENDPOINT_USAGE_IMPLICIT_FB)
-		return -ENOENT;
+		return false;
 	*ep = epd->bEndpointAddress;
-	return 0;
+	*altsp = alts;
+	return true;
 }
 
 /* Setup an implicit feedback endpoint from a quirk. Returns 0 if no quirk
@@ -375,6 +412,19 @@  static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
 		return 0;
 	}
 
+	/* Generic UAC2 implicit feedback */
+	if (attr == USB_ENDPOINT_SYNC_ASYNC &&
+	    altsd->bInterfaceClass == USB_CLASS_AUDIO &&
+	    altsd->bInterfaceProtocol == UAC_VERSION_2 &&
+	    altsd->bNumEndpoints == 1) {
+		ifnum = altsd->bInterfaceNumber + 1;
+		if (search_generic_implicit_fb(dev, ifnum,
+					       altsd->bAlternateSetting,
+					       &alts, &ep))
+			goto add_sync_ep;
+	}
+
+	/* Roland/BOSS implicit feedback with vendor spec class */
 	if (attr == USB_ENDPOINT_SYNC_ASYNC &&
 	    altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
 	    altsd->bInterfaceProtocol == 2 &&
@@ -382,9 +432,8 @@  static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
 	    USB_ID_VENDOR(subs->stream->chip->usb_id) == 0x0582 /* Roland */ &&
 	    search_roland_implicit_fb(dev, altsd->bInterfaceNumber + 1,
 				      altsd->bAlternateSetting,
-				      &alts, &ep) >= 0) {
+				      &alts, &ep))
 		goto add_sync_ep;
-	}
 
 	/* No quirk */
 	return 0;