]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/media/video/uvc/uvc_driver.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable
[linux-2.6-omap-h63xx.git] / drivers / media / video / uvc / uvc_driver.c
index d7ad060640bc5758fd526d7864fc94adcab8a52c..b12873265cc56c5c811a6cba514305f8e6534a3d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *      uvc_driver.c  --  USB Video Class driver
  *
- *      Copyright (C) 2005-2008
+ *      Copyright (C) 2005-2009
  *          Laurent Pinchart (laurent.pinchart@skynet.be)
  *
  *      This program is free software; you can redistribute it and/or modify
@@ -12,8 +12,8 @@
  */
 
 /*
- * This driver aims to support video input devices compliant with the 'USB
- * Video Class' specification.
+ * This driver aims to support video input and ouput devices compliant with the
+ * 'USB Video Class' specification.
  *
  * The driver doesn't support the deprecated v4l1 interface. It implements the
  * mmap capture method only, and doesn't do any image format conversion in
@@ -24,7 +24,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/version.h>
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/usb.h>
@@ -32,6 +31,7 @@
 #include <linux/vmalloc.h>
 #include <linux/wait.h>
 #include <asm/atomic.h>
+#include <asm/unaligned.h>
 
 #include <media/v4l2-common.h>
 
 #define DRIVER_VERSION         "v0.1.0"
 #endif
 
+unsigned int uvc_no_drop_param;
 static unsigned int uvc_quirks_param;
 unsigned int uvc_trace_param;
 
 /* ------------------------------------------------------------------------
- * Control, formats, ...
+ * Video formats
  */
 
 static struct uvc_format_desc uvc_fmts[] = {
@@ -288,8 +289,10 @@ static int uvc_parse_format(struct uvc_device *dev,
        struct uvc_format_desc *fmtdesc;
        struct uvc_frame *frame;
        const unsigned char *start = buffer;
+       unsigned char *_buffer;
        unsigned int interval;
        unsigned int i, n;
+       int _buflen;
        __u8 ftype;
 
        format->type = buffer[2];
@@ -410,12 +413,20 @@ static int uvc_parse_format(struct uvc_device *dev,
        buflen -= buffer[0];
        buffer += buffer[0];
 
+       /* Count the number of frame descriptors to test the bFrameIndex
+        * field when parsing the descriptors. We can't rely on the
+        * bNumFrameDescriptors field as some cameras don't initialize it
+        * properly.
+        */
+       for (_buflen = buflen, _buffer = buffer;
+            _buflen > 2 && _buffer[2] == ftype;
+            _buflen -= _buffer[0], _buffer += _buffer[0])
+               format->nframes++;
+
        /* Parse the frame descriptors. Only uncompressed, MJPEG and frame
         * based formats have frame descriptors.
         */
        while (buflen > 2 && buffer[2] == ftype) {
-               frame = &format->frame[format->nframes];
-
                if (ftype != VS_FRAME_FRAME_BASED)
                        n = buflen > 25 ? buffer[25] : 0;
                else
@@ -430,29 +441,39 @@ static int uvc_parse_format(struct uvc_device *dev,
                        return -EINVAL;
                }
 
+               if (buffer[3] - 1 >= format->nframes) {
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+                              "interface %d frame index %u out of range\n",
+                              dev->udev->devnum, alts->desc.bInterfaceNumber,
+                              buffer[3]);
+                       return -EINVAL;
+               }
+
+               frame = &format->frame[buffer[3] - 1];
+
                frame->bFrameIndex = buffer[3];
                frame->bmCapabilities = buffer[4];
-               frame->wWidth = le16_to_cpup((__le16 *)&buffer[5]);
-               frame->wHeight = le16_to_cpup((__le16 *)&buffer[7]);
-               frame->dwMinBitRate = le32_to_cpup((__le32 *)&buffer[9]);
-               frame->dwMaxBitRate = le32_to_cpup((__le32 *)&buffer[13]);
+               frame->wWidth = get_unaligned_le16(&buffer[5]);
+               frame->wHeight = get_unaligned_le16(&buffer[7]);
+               frame->dwMinBitRate = get_unaligned_le32(&buffer[9]);
+               frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]);
                if (ftype != VS_FRAME_FRAME_BASED) {
                        frame->dwMaxVideoFrameBufferSize =
-                               le32_to_cpup((__le32 *)&buffer[17]);
+                               get_unaligned_le32(&buffer[17]);
                        frame->dwDefaultFrameInterval =
-                               le32_to_cpup((__le32 *)&buffer[21]);
+                               get_unaligned_le32(&buffer[21]);
                        frame->bFrameIntervalType = buffer[25];
                } else {
                        frame->dwMaxVideoFrameBufferSize = 0;
                        frame->dwDefaultFrameInterval =
-                               le32_to_cpup((__le32 *)&buffer[17]);
+                               get_unaligned_le32(&buffer[17]);
                        frame->bFrameIntervalType = buffer[21];
                }
                frame->dwFrameInterval = *intervals;
 
                /* Several UVC chipsets screw up dwMaxVideoFrameBufferSize
                 * completely. Observed behaviours range from setting the
-                * value to 1.1x the actual frame size of hardwiring the
+                * value to 1.1x the actual frame size to hardwiring the
                 * 16 low bits to 0. This results in a higher than necessary
                 * memory usage as well as a wrong image size information. For
                 * uncompressed formats this can be fixed by computing the
@@ -465,10 +486,10 @@ static int uvc_parse_format(struct uvc_device *dev,
                /* Some bogus devices report dwMinFrameInterval equal to
                 * dwMaxFrameInterval and have dwFrameIntervalStep set to
                 * zero. Setting all null intervals to 1 fixes the problem and
-                * some other divisions by zero which could happen.
+                * some other divisions by zero that could happen.
                 */
                for (i = 0; i < n; ++i) {
-                       interval = le32_to_cpup((__le32 *)&buffer[26+4*i]);
+                       interval = get_unaligned_le32(&buffer[26+4*i]);
                        *(*intervals)++ = interval ? interval : 1;
                }
 
@@ -486,7 +507,6 @@ static int uvc_parse_format(struct uvc_device *dev,
                        10000000/frame->dwDefaultFrameInterval,
                        (100000000/frame->dwDefaultFrameInterval)%10);
 
-               format->nframes++;
                buflen -= buffer[0];
                buffer += buffer[0];
        }
@@ -588,46 +608,55 @@ static int uvc_parse_streaming(struct uvc_device *dev,
        }
 
        /* Parse the header descriptor. */
-       if (buffer[2] == VS_OUTPUT_HEADER) {
+       switch (buffer[2]) {
+       case VS_OUTPUT_HEADER:
+               streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+               size = 9;
+               break;
+
+       case VS_INPUT_HEADER:
+               streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               size = 13;
+               break;
+
+       default:
                uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
-                       "%d OUTPUT HEADER descriptor is not supported.\n",
-                       dev->udev->devnum, alts->desc.bInterfaceNumber);
+                       "%d HEADER descriptor not found.\n", dev->udev->devnum,
+                       alts->desc.bInterfaceNumber);
                goto error;
-       } else if (buffer[2] == VS_INPUT_HEADER) {
-               p = buflen >= 5 ? buffer[3] : 0;
-               n = buflen >= 12 ? buffer[12] : 0;
+       }
 
-               if (buflen < 13 + p*n || buffer[2] != VS_INPUT_HEADER) {
-                       uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
-                               "interface %d INPUT HEADER descriptor is "
-                               "invalid.\n", dev->udev->devnum,
-                               alts->desc.bInterfaceNumber);
-                       goto error;
-               }
+       p = buflen >= 4 ? buffer[3] : 0;
+       n = buflen >= size ? buffer[size-1] : 0;
+
+       if (buflen < size + p*n) {
+               uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+                       "interface %d HEADER descriptor is invalid.\n",
+                       dev->udev->devnum, alts->desc.bInterfaceNumber);
+               goto error;
+       }
 
-               streaming->header.bNumFormats = p;
-               streaming->header.bEndpointAddress = buffer[6];
+       streaming->header.bNumFormats = p;
+       streaming->header.bEndpointAddress = buffer[6];
+       if (buffer[2] == VS_INPUT_HEADER) {
                streaming->header.bmInfo = buffer[7];
                streaming->header.bTerminalLink = buffer[8];
                streaming->header.bStillCaptureMethod = buffer[9];
                streaming->header.bTriggerSupport = buffer[10];
                streaming->header.bTriggerUsage = buffer[11];
-               streaming->header.bControlSize = n;
-
-               streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL);
-               if (streaming->header.bmaControls == NULL) {
-                       ret = -ENOMEM;
-                       goto error;
-               }
-
-               memcpy(streaming->header.bmaControls, &buffer[13], p*n);
        } else {
-               uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
-                       "%d HEADER descriptor not found.\n", dev->udev->devnum,
-                       alts->desc.bInterfaceNumber);
+               streaming->header.bTerminalLink = buffer[7];
+       }
+       streaming->header.bControlSize = n;
+
+       streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL);
+       if (streaming->header.bmaControls == NULL) {
+               ret = -ENOMEM;
                goto error;
        }
 
+       memcpy(streaming->header.bmaControls, &buffer[size], p*n);
+
        buflen -= buffer[0];
        buffer += buffer[0];
 
@@ -813,8 +842,7 @@ static int uvc_parse_vendor_control(struct uvc_device *dev,
                unit->type = VC_EXTENSION_UNIT;
                memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
                unit->extension.bNumControls = buffer[20];
-               unit->extension.bNrInPins =
-                       le16_to_cpup((__le16 *)&buffer[21]);
+               unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]);
                unit->extension.baSourceID = (__u8 *)unit + sizeof *unit;
                memcpy(unit->extension.baSourceID, &buffer[22], p);
                unit->extension.bControlSize = buffer[22+p];
@@ -858,8 +886,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
                        return -EINVAL;
                }
 
-               dev->uvc_version = le16_to_cpup((__le16 *)&buffer[3]);
-               dev->clock_frequency = le32_to_cpup((__le32 *)&buffer[7]);
+               dev->uvc_version = get_unaligned_le16(&buffer[3]);
+               dev->clock_frequency = get_unaligned_le32(&buffer[7]);
 
                /* Parse all USB Video Streaming interfaces. */
                for (i = 0; i < n; ++i) {
@@ -886,7 +914,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
                /* Make sure the terminal type MSB is not null, otherwise it
                 * could be confused with a unit.
                 */
-               type = le16_to_cpup((__le16 *)&buffer[4]);
+               type = get_unaligned_le16(&buffer[4]);
                if ((type & 0xff00) == 0) {
                        uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
                                "interface %d INPUT_TERMINAL %d has invalid "
@@ -928,11 +956,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
                        term->camera.bControlSize = n;
                        term->camera.bmControls = (__u8 *)term + sizeof *term;
                        term->camera.wObjectiveFocalLengthMin =
-                               le16_to_cpup((__le16 *)&buffer[8]);
+                               get_unaligned_le16(&buffer[8]);
                        term->camera.wObjectiveFocalLengthMax =
-                               le16_to_cpup((__le16 *)&buffer[10]);
+                               get_unaligned_le16(&buffer[10]);
                        term->camera.wOcularFocalLength =
-                               le16_to_cpup((__le16 *)&buffer[12]);
+                               get_unaligned_le16(&buffer[12]);
                        memcpy(term->camera.bmControls, &buffer[15], n);
                } else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) {
                        term->media.bControlSize = n;
@@ -968,7 +996,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
                /* Make sure the terminal type MSB is not null, otherwise it
                 * could be confused with a unit.
                 */
-               type = le16_to_cpup((__le16 *)&buffer[4]);
+               type = get_unaligned_le16(&buffer[4]);
                if ((type & 0xff00) == 0) {
                        uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
                                "interface %d OUTPUT_TERMINAL %d has invalid "
@@ -1042,7 +1070,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
                unit->type = buffer[2];
                unit->processing.bSourceID = buffer[4];
                unit->processing.wMaxMultiplier =
-                       le16_to_cpup((__le16 *)&buffer[5]);
+                       get_unaligned_le16(&buffer[5]);
                unit->processing.bControlSize = buffer[7];
                unit->processing.bmControls = (__u8 *)unit + sizeof *unit;
                memcpy(unit->processing.bmControls, &buffer[8], n);
@@ -1077,8 +1105,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev,
                unit->type = buffer[2];
                memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
                unit->extension.bNumControls = buffer[20];
-               unit->extension.bNrInPins =
-                       le16_to_cpup((__le16 *)&buffer[21]);
+               unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]);
                unit->extension.baSourceID = (__u8 *)unit + sizeof *unit;
                memcpy(unit->extension.baSourceID, &buffer[22], p);
                unit->extension.bControlSize = buffer[22+p];
@@ -1128,8 +1155,13 @@ next_descriptor:
                buffer += buffer[0];
        }
 
-       /* Check if the optional status endpoint is present. */
-       if (alts->desc.bNumEndpoints == 1) {
+       /* Check if the optional status endpoint is present. Built-in iSight
+        * webcams have an interrupt endpoint but spit proprietary data that
+        * don't conform to the UVC status endpoint messages. Don't try to
+        * handle the interrupt endpoint for those cameras.
+        */
+       if (alts->desc.bNumEndpoints == 1 &&
+           !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) {
                struct usb_host_endpoint *ep = &alts->endpoint[0];
                struct usb_endpoint_descriptor *desc = &ep->desc;
 
@@ -1167,13 +1199,13 @@ static void uvc_unregister_video(struct uvc_device *dev)
  * Scan the UVC descriptors to locate a chain starting at an Output Terminal
  * and containing the following units:
  *
- * - a USB Streaming Output Terminal
+ * - one Output Terminal (USB Streaming or Display)
  * - zero or one Processing Unit
  * - zero, one or mode single-input Selector Units
  * - zero or one multiple-input Selector Units, provided all inputs are
  *   connected to input terminals
  * - zero, one or mode single-input Extension Units
- * - one Camera Input Terminal, or one or more External terminals.
+ * - one or more Input Terminals (Camera, External or USB Streaming)
  *
  * A side forward scan is made on each detected entity to check for additional
  * extension units.
@@ -1234,6 +1266,26 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
                list_add_tail(&entity->chain, &video->iterms);
                break;
 
+       case TT_STREAMING:
+               if (uvc_trace_param & UVC_TRACE_PROBE)
+                       printk(" <- IT %d\n", entity->id);
+
+               if (!UVC_ENTITY_IS_ITERM(entity)) {
+                       uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
+                               "terminal %u.\n", entity->id);
+                       return -1;
+               }
+
+               if (video->sterm != NULL) {
+                       uvc_trace(UVC_TRACE_DESCR, "Found multiple streaming "
+                               "entities in chain.\n");
+                       return -1;
+               }
+
+               list_add_tail(&entity->chain, &video->iterms);
+               video->sterm = entity;
+               break;
+
        default:
                uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type "
                        "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
@@ -1344,6 +1396,10 @@ static int uvc_scan_chain(struct uvc_video_device *video)
 
        entity = video->oterm;
        uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id);
+
+       if (UVC_ENTITY_TYPE(entity) == TT_STREAMING)
+               video->sterm = entity;
+
        id = entity->output.bSourceID;
        while (id != 0) {
                prev = entity;
@@ -1372,8 +1428,11 @@ static int uvc_scan_chain(struct uvc_video_device *video)
                        return id;
        }
 
-       /* Initialize the video buffers queue. */
-       uvc_queue_init(&video->queue);
+       if (video->sterm == NULL) {
+               uvc_trace(UVC_TRACE_DESCR, "No streaming entity found in "
+                       "chain.\n");
+               return -1;
+       }
 
        return 0;
 }
@@ -1384,7 +1443,8 @@ static int uvc_scan_chain(struct uvc_video_device *video)
  * The driver currently supports a single video device per control interface
  * only. The terminal and units must match the following structure:
  *
- * ITT_CAMERA -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING
+ * ITT_* -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING
+ * TT_STREAMING -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> OTT_*
  *
  * The Extension Units, if present, must have a single input pin. The
  * Processing Unit and Extension Units can be in any order. Additional
@@ -1401,7 +1461,7 @@ static int uvc_register_video(struct uvc_device *dev)
        list_for_each_entry(term, &dev->entities, list) {
                struct uvc_streaming *streaming;
 
-               if (UVC_ENTITY_TYPE(term) != TT_STREAMING)
+               if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term))
                        continue;
 
                memset(&dev->video, 0, sizeof dev->video);
@@ -1414,7 +1474,8 @@ static int uvc_register_video(struct uvc_device *dev)
                        continue;
 
                list_for_each_entry(streaming, &dev->streaming, list) {
-                       if (streaming->header.bTerminalLink == term->id) {
+                       if (streaming->header.bTerminalLink ==
+                           dev->video.sterm->id) {
                                dev->video.streaming = streaming;
                                found = 1;
                                break;
@@ -1440,6 +1501,9 @@ static int uvc_register_video(struct uvc_device *dev)
                printk(" -> %d).\n", dev->video.oterm->id);
        }
 
+       /* Initialize the video buffers queue. */
+       uvc_queue_init(&dev->video.queue, dev->video.streaming->type);
+
        /* Initialize the streaming interface with default streaming
         * parameters.
         */
@@ -1466,10 +1530,6 @@ static int uvc_register_video(struct uvc_device *dev)
 
        /* Set the driver data before calling video_register_device, otherwise
         * uvc_v4l2_open might race us.
-        *
-        * FIXME: usb_set_intfdata hasn't been called so far. Is that a
-        *        problem ? Does any function which could be called here get
-        *        a pointer to the usb_interface ?
         */
        dev->video.vdev = vdev;
        video_set_drvdata(vdev, &dev->video);
@@ -1504,7 +1564,7 @@ void uvc_delete(struct kref *kref)
        struct uvc_device *dev = container_of(kref, struct uvc_device, kref);
        struct list_head *p, *n;
 
-       /* Unregister the video device */
+       /* Unregister the video device. */
        uvc_unregister_video(dev);
        usb_put_intf(dev->intf);
        usb_put_dev(dev->udev);
@@ -1547,7 +1607,7 @@ static int uvc_probe(struct usb_interface *intf,
                uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
                                udev->devpath);
 
-       /* Allocate memory for the device and initialize it */
+       /* Allocate memory for the device and initialize it. */
        if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL)
                return -ENOMEM;
 
@@ -1568,14 +1628,14 @@ static int uvc_probe(struct usb_interface *intf,
                        le16_to_cpu(udev->descriptor.idVendor),
                        le16_to_cpu(udev->descriptor.idProduct));
 
-       /* Parse the Video Class control descriptor */
+       /* Parse the Video Class control descriptor. */
        if (uvc_parse_control(dev) < 0) {
                uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC "
                        "descriptors.\n");
                goto error;
        }
 
-       uvc_printk(KERN_INFO, "Found UVC %u.%02u device %s (%04x:%04x)\n",
+       uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n",
                dev->uvc_version >> 8, dev->uvc_version & 0xff,
                udev->product ? udev->product : "<unnamed>",
                le16_to_cpu(udev->descriptor.idVendor),
@@ -1588,18 +1648,18 @@ static int uvc_probe(struct usb_interface *intf,
                        "linux-uvc-devel mailing list.\n");
        }
 
-       /* Initialize controls */
+       /* Initialize controls. */
        if (uvc_ctrl_init_device(dev) < 0)
                goto error;
 
-       /* Register the video devices */
+       /* Register the video devices. */
        if (uvc_register_video(dev) < 0)
                goto error;
 
-       /* Save our data pointer in the interface data */
+       /* Save our data pointer in the interface data. */
        usb_set_intfdata(intf, dev);
 
-       /* Initialize the interrupt URB */
+       /* Initialize the interrupt URB. */
        if ((ret = uvc_status_init(dev)) < 0) {
                uvc_printk(KERN_INFO, "Unable to initialize the status "
                        "endpoint (%d), status interrupt will not be "
@@ -1707,24 +1767,6 @@ static int uvc_reset_resume(struct usb_interface *intf)
  * though they are compliant.
  */
 static struct usb_device_id uvc_ids[] = {
-       /* ALi M5606 (Clevo M540SR) */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x0402,
-         .idProduct            = 0x5606,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Creative Live! Optia */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x041e,
-         .idProduct            = 0x4057,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
        /* Microsoft Lifecam NX-6000 */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1792,33 +1834,24 @@ static struct usb_device_id uvc_ids[] = {
          .bInterfaceSubClass   = 1,
          .bInterfaceProtocol   = 0 },
        /* Apple Built-In iSight */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
          .idVendor             = 0x05ac,
          .idProduct            = 0x8501,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
          .driver_info          = UVC_QUIRK_PROBE_MINMAX
                                | UVC_QUIRK_BUILTIN_ISIGHT },
        /* Genesys Logic USB 2.0 PC Camera */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x05e3,
-         .idProduct            = 0x0505,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_STREAM_NO_FID },
-       /* Silicon Motion SM371 */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x090c,
-         .idProduct            = 0xb371,
+         .idVendor             = 0x05e3,
+         .idProduct            = 0x0505,
          .bInterfaceClass      = USB_CLASS_VIDEO,
          .bInterfaceSubClass   = 1,
          .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
+         .driver_info          = UVC_QUIRK_STREAM_NO_FID },
        /* MT6227 */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1837,6 +1870,15 @@ static struct usb_device_id uvc_ids[] = {
          .bInterfaceSubClass   = 1,
          .bInterfaceProtocol   = 0,
          .driver_info          = UVC_QUIRK_STREAM_NO_FID },
+       /* Syntek (Samsung Q310) */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x174f,
+         .idProduct            = 0x5931,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_STREAM_NO_FID },
        /* Asus F9SG */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1855,6 +1897,15 @@ static struct usb_device_id uvc_ids[] = {
          .bInterfaceSubClass   = 1,
          .bInterfaceProtocol   = 0,
          .driver_info          = UVC_QUIRK_STREAM_NO_FID },
+       /* Lenovo Thinkpad SL500 */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x17ef,
+         .idProduct            = 0x480b,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_STREAM_NO_FID },
        /* Ecamm Pico iMage */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1884,106 +1935,8 @@ static struct usb_device_id uvc_ids[] = {
          .bInterfaceSubClass   = 1,
          .bInterfaceProtocol   = 0,
          .driver_info          = UVC_QUIRK_PROBE_MINMAX
-                               | UVC_QUIRK_IGNORE_SELECTOR_UNIT},
-       /* Acer OEM Webcam - Unknown vendor */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0100,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Packard Bell OEM Webcam - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0101,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Acer Crystal Eye webcam - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0102,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Compaq Presario B1200 - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0104,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Acer Travelmate 7720 - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0105,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Medion Akoya Mini E1210 - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0141,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Acer OrbiCam - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0200,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /*  Fujitsu Amilo SI2636 - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0202,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /*  Advent 4211 - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0203,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0300,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Clevo M570TU - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0303,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
+                               | UVC_QUIRK_IGNORE_SELECTOR_UNIT
+                               | UVC_QUIRK_PRUNE_CONTROLS },
        /* Generic USB Video Class */
        { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
        {}
@@ -2029,6 +1982,8 @@ static void __exit uvc_cleanup(void)
 module_init(uvc_init);
 module_exit(uvc_cleanup);
 
+module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
 module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
 MODULE_PARM_DESC(quirks, "Forced device quirks");
 module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);