]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ALSA: snd-usb: Add quirks for Playback Designs devices
authorDaniel Mack <zonque@gmail.com>
Tue, 4 Sep 2012 08:23:07 +0000 (10:23 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 12 Oct 2012 20:50:27 +0000 (05:50 +0900)
commit 2b58fd5b3193fd3af3d15114d95706087d25a7fe upstream.

Playback Designs' USB devices have some hardware limitations on their
USB interface. In particular:

 - They need a 20ms delay after each class compliant request as the
   hardware ACKs the USB packets before the device is actually ready
   for the next command. Sending data immediately will result in buffer
   overflows in the hardware.
 - The devices send bogus feedback data at the start of each stream
   which confuse the feedback format auto-detection.

This patch introduces a new quirks hook that is called after each
control packet and which adds a delay for all devices that match
Playback Designs' USB VID for now.

In addition, it adds a counter to snd_usb_endpoint to drop received
packets on the floor. Another new quirks function that is called once
an endpoint is started initializes that counter for these devices on
their sync endpoint.

Signed-off-by: Daniel Mack <zonque@gmail.com>
Reported-and-tested-by: Andreas Koch <andreas@akdesigninc.com>
Supported-by: Demian Martin <demianm_1@yahoo.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
sound/usb/card.h
sound/usb/endpoint.c
sound/usb/helper.c
sound/usb/quirks.c
sound/usb/quirks.h

index 2b9fffff23b62a5449d39f2a4b03cbaadb1529a6..d9d2b5a3bf2fcb0750860fc12fbcaa1cc185ecab 100644 (file)
@@ -92,6 +92,8 @@ struct snd_usb_endpoint {
        unsigned char silence_value;
        unsigned int stride;
        int iface, alt_idx;
+       int skip_packets;               /* quirks for devices to ignore the first n packets
+                                          in a stream */
 
        spinlock_t lock;
        struct list_head list;
index 060dccb9ec75513def50e6f8be32f60f4f3b05f1..d9de6670b97ef85bcb14547b6e5302319afeba3d 100644 (file)
@@ -31,6 +31,7 @@
 #include "card.h"
 #include "endpoint.h"
 #include "pcm.h"
+#include "quirks.h"
 
 #define EP_FLAG_ACTIVATED      0
 #define EP_FLAG_RUNNING                1
@@ -170,6 +171,11 @@ static void retire_inbound_urb(struct snd_usb_endpoint *ep,
 {
        struct urb *urb = urb_ctx->urb;
 
+       if (unlikely(ep->skip_packets > 0)) {
+               ep->skip_packets--;
+               return;
+       }
+
        if (ep->sync_slave)
                snd_usb_handle_sync_urb(ep->sync_slave, ep, urb);
 
@@ -828,6 +834,8 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, int can_sleep)
        ep->unlink_mask = 0;
        ep->phase = 0;
 
+       snd_usb_endpoint_start_quirk(ep);
+
        /*
         * If this endpoint has a data endpoint as implicit feedback source,
         * don't start the urbs here. Instead, mark them all as available,
index 9eed8f40b179dd199702c1247dd65bdd0b56ff35..c1db28f874c285d12f2ee310a57ce34daa54e301 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "usbaudio.h"
 #include "helper.h"
+#include "quirks.h"
 
 /*
  * combine bytes and get an integer value
@@ -97,6 +98,10 @@ int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
                memcpy(data, buf, size);
                kfree(buf);
        }
+
+       snd_usb_ctl_msg_quirk(dev, pipe, request, requesttype,
+                             value, index, data, size);
+
        return err;
 }
 
index 27817266867ae2c17b09f4ea85b6522a9d39e4ea..0f58b4b6d7023d0e4c47734216e276132c2fbe80 100644 (file)
@@ -761,3 +761,27 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
        }
 }
 
+void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
+{
+       /*
+        * "Playback Design" products send bogus feedback data at the start
+        * of the stream. Ignore them.
+        */
+       if ((le16_to_cpu(ep->chip->dev->descriptor.idVendor) == 0x23ba) &&
+           ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
+               ep->skip_packets = 4;
+}
+
+void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
+                          __u8 request, __u8 requesttype, __u16 value,
+                          __u16 index, void *data, __u16 size)
+{
+       /*
+        * "Playback Design" products need a 20ms delay after each
+        * class compliant request
+        */
+       if ((le16_to_cpu(dev->descriptor.idVendor) == 0x23ba) &&
+           (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
+               mdelay(20);
+}
+
index 03e5e94098cdc8f97d7510c4090204bc1c7bce03..0ca9e91067a664e6544f22161cf026c118469860 100644 (file)
@@ -1,6 +1,10 @@
 #ifndef __USBAUDIO_QUIRKS_H
 #define __USBAUDIO_QUIRKS_H
 
+struct audioformat;
+struct snd_usb_endpoint;
+struct snd_usb_substream;
+
 int snd_usb_create_quirk(struct snd_usb_audio *chip,
                         struct usb_interface *iface,
                         struct usb_driver *driver,
@@ -20,4 +24,10 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
 int snd_usb_is_big_endian_format(struct snd_usb_audio *chip,
                                 struct audioformat *fp);
 
+void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep);
+
+void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
+                          __u8 request, __u8 requesttype, __u16 value,
+                          __u16 index, void *data, __u16 size);
+
 #endif /* __USBAUDIO_QUIRKS_H */