Merge branch 'i2c-fix' of git://aeryn.fluff.org.uk/bjdooks/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 3 Jul 2008 02:26:38 +0000 (19:26 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 3 Jul 2008 02:26:38 +0000 (19:26 -0700)
* 'i2c-fix' of git://aeryn.fluff.org.uk/bjdooks/linux:
  I2C: S3C2410: Add MODULE_ALIAS() for s3c2440 device.
  I2C: S3C2410: Fixup error codes returned rom a transfer.
  I2C: S3C2410: Check ACK on byte transmission

120 files changed:
Documentation/i2c/writing-clients
Documentation/networking/ip-sysctl.txt
Documentation/networking/s2io.txt
MAINTAINERS
arch/arm/common/dmabounce.c
arch/arm/mach-omap2/gpmc.c
arch/arm/mach-realview/platsmp.c
arch/arm/plat-omap/dma.c
arch/ia64/kernel/setup.c
arch/ia64/kernel/time.c
arch/powerpc/boot/Makefile
arch/powerpc/kernel/legacy_serial.c
arch/powerpc/platforms/52xx/lite5200_pm.c
arch/x86/Kconfig
block/as-iosched.c
drivers/char/drm/i915_irq.c
drivers/char/tty_io.c
drivers/connector/connector.c
drivers/ide/Kconfig
drivers/ide/arm/Makefile
drivers/ide/arm/bast-ide.c [new file with mode: 0644]
drivers/ide/ide-proc.c
drivers/input/ff-core.c
drivers/md/dm-crypt.c
drivers/md/md.c
drivers/md/raid10.c
drivers/md/raid5.c
drivers/media/video/Kconfig
drivers/media/video/Makefile
drivers/media/video/uvc/Makefile [new file with mode: 0644]
drivers/media/video/uvc/uvc_ctrl.c [new file with mode: 0644]
drivers/media/video/uvc/uvc_driver.c [new file with mode: 0644]
drivers/media/video/uvc/uvc_isight.c [new file with mode: 0644]
drivers/media/video/uvc/uvc_queue.c [new file with mode: 0644]
drivers/media/video/uvc/uvc_status.c [new file with mode: 0644]
drivers/media/video/uvc/uvc_v4l2.c [new file with mode: 0644]
drivers/media/video/uvc/uvc_video.c [new file with mode: 0644]
drivers/media/video/uvc/uvcvideo.h [new file with mode: 0644]
drivers/net/3c59x.c
drivers/net/e100.c
drivers/net/e1000/e1000_ethtool.c
drivers/net/e1000e/netdev.c
drivers/net/hamradio/dmascc.c
drivers/net/igb/igb_main.c
drivers/net/ipg.c
drivers/net/ixgbe/ixgbe_main.c
drivers/net/netxen/netxen_nic_main.c
drivers/net/pcmcia/axnet_cs.c
drivers/net/pcmcia/pcnet_cs.c
drivers/net/qla3xxx.c
drivers/net/r6040.c
drivers/net/s2io.c
drivers/net/s2io.h
drivers/net/tc35815.c
drivers/net/wan/x25_asy.c
drivers/net/wireless/b43/leds.c
drivers/net/wireless/b43/main.c
drivers/net/wireless/b43legacy/dma.c
drivers/net/wireless/b43legacy/main.c
drivers/net/wireless/hostap/hostap_80211_rx.c
drivers/net/wireless/hostap/hostap_ap.c
drivers/net/wireless/hostap/hostap_cs.c
drivers/net/wireless/hostap/hostap_hw.c
drivers/net/wireless/hostap/hostap_main.c
drivers/net/wireless/iwlwifi/iwl3945-base.c
drivers/net/wireless/iwlwifi/iwl4965-base.c
drivers/net/wireless/prism54/islpci_eth.c
drivers/net/wireless/rt2x00/rt2500usb.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00dev.c
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rt2x00/rt73usb.c
drivers/rtc/rtc-sa1100.c
drivers/scsi/esp_scsi.c
drivers/scsi/ses.c
drivers/video/pxafb.c
fs/buffer.c
include/linux/fs.h
include/linux/i2c.h
include/linux/inet_lro.h
include/linux/netdevice.h
include/net/mac80211.h
include/net/sch_generic.h
kernel/rcuclassic.c
kernel/sched.c
lib/ts_bm.c
net/core/dev.c
net/core/fib_rules.c
net/core/filter.c
net/core/skbuff.c
net/ipv4/inet_fragment.c
net/ipv4/inet_lro.c
net/ipv4/ip_fragment.c
net/ipv4/tcp.c
net/ipv4/tcp_ipv4.c
net/ipv6/netfilter/ip6table_mangle.c
net/ipv6/netfilter/nf_conntrack_reasm.c
net/ipv6/reassembly.c
net/ipv6/route.c
net/ipv6/tcp_ipv6.c
net/mac80211/key.c
net/mac80211/wext.c
net/mac80211/wme.c
net/netfilter/nf_conntrack_proto_tcp.c
net/netlabel/netlabel_unlabeled.c
net/netlink/af_netlink.c
net/netlink/attr.c
net/sched/Kconfig
net/sched/sch_api.c
net/sched/sch_atm.c
net/sched/sch_cbq.c
net/sched/sch_dsmark.c
net/sched/sch_generic.c
net/sched/sch_hfsc.c
net/sched/sch_htb.c
net/sched/sch_ingress.c
net/sched/sch_prio.c
net/sched/sch_sfq.c
net/unix/af_unix.c
net/wireless/reg.c

index ee75cba..d4cd412 100644 (file)
@@ -25,12 +25,23 @@ routines, and should be zero-initialized except for fields with data you
 provide.  A client structure holds device-specific information like the
 driver model device node, and its I2C address.
 
+/* iff driver uses driver model ("new style") binding model: */
+
+static struct i2c_device_id foo_idtable[] = {
+       { "foo", my_id_for_foo },
+       { "bar", my_id_for_bar },
+       { }
+};
+
+MODULE_DEVICE_TABLE(i2c, foo_idtable);
+
 static struct i2c_driver foo_driver = {
        .driver = {
                .name   = "foo",
        },
 
        /* iff driver uses driver model ("new style") binding model: */
+       .id_table       = foo_ids,
        .probe          = foo_probe,
        .remove         = foo_remove,
 
@@ -173,10 +184,9 @@ handle may be used during foo_probe().  If foo_probe() reports success
 (zero not a negative status code) it may save the handle and use it until
 foo_remove() returns.  That binding model is used by most Linux drivers.
 
-Drivers match devices when i2c_client.driver_name and the driver name are
-the same; this approach is used in several other busses that don't have
-device typing support in the hardware.  The driver and module name should
-match, so hotplug/coldplug mechanisms will modprobe the driver.
+The probe function is called when an entry in the id_table name field
+matches the device's name. It is passed the entry that was matched so
+the driver knows which one in the table matched.
 
 
 Device Creation (Standard driver model)
index 17a6e46..17f1f91 100644 (file)
@@ -81,23 +81,23 @@ inet_peer_minttl - INTEGER
        Minimum time-to-live of entries.  Should be enough to cover fragment
        time-to-live on the reassembling side.  This minimum time-to-live  is
        guaranteed if the pool size is less than inet_peer_threshold.
-       Measured in jiffies(1).
+       Measured in seconds.
 
 inet_peer_maxttl - INTEGER
        Maximum time-to-live of entries.  Unused entries will expire after
        this period of time if there is no memory pressure on the pool (i.e.
        when the number of entries in the pool is very small).
-       Measured in jiffies(1).
+       Measured in seconds.
 
 inet_peer_gc_mintime - INTEGER
        Minimum interval between garbage collection passes.  This interval is
        in effect under high memory pressure on the pool.
-       Measured in jiffies(1).
+       Measured in seconds.
 
 inet_peer_gc_maxtime - INTEGER
        Minimum interval between garbage collection passes.  This interval is
        in effect under low (or absent) memory pressure on the pool.
-       Measured in jiffies(1).
+       Measured in seconds.
 
 TCP variables: 
 
@@ -794,10 +794,6 @@ tag - INTEGER
        Allows you to write a number, which can be used as required.
        Default value is 0.
 
-(1) Jiffie: internal timeunit for the kernel. On the i386 1/100s, on the
-Alpha 1/1024s. See the HZ define in /usr/include/asm/param.h for the exact
-value on your system. 
-
 Alexey Kuznetsov.
 kuznet@ms2.inr.ac.ru
 
index 4bde53e..1e28e2d 100644 (file)
@@ -83,9 +83,9 @@ Valid range: Limited by memory on system
 Default: 30 
 
 e. intr_type
-Specifies interrupt type. Possible values 1(INTA), 2(MSI), 3(MSI-X)
-Valid range: 1-3
-Default: 
+Specifies interrupt type. Possible values 0(INTA), 2(MSI-X)
+Valid values: 0, 2
+Default: 2
 
 5.  Performance suggestions
 General:
index 8f0ec46..e6c06fa 100644 (file)
@@ -4314,6 +4314,14 @@ L:       netdev@vger.kernel.org
 W:     http://www.linux-usb.org/usbnet
 S:     Maintained
 
+USB VIDEO CLASS
+P:     Laurent Pinchart
+M:     laurent.pinchart@skynet.be
+L:     linx-uvc-devel@berlios.de
+L:     video4linux-list@redhat.com
+W:     http://linux-uvc.berlios.de
+S:     Maintained
+
 USB W996[87]CF DRIVER
 P:     Luca Risolia
 M:     luca.risolia@studio.unibo.it
index 52fc6a8..2744673 100644 (file)
@@ -650,7 +650,8 @@ EXPORT_SYMBOL(dma_map_sg);
 EXPORT_SYMBOL(dma_unmap_sg);
 EXPORT_SYMBOL(dma_sync_single_for_cpu);
 EXPORT_SYMBOL(dma_sync_single_for_device);
-EXPORT_SYMBOL(dma_sync_sg);
+EXPORT_SYMBOL(dma_sync_sg_for_cpu);
+EXPORT_SYMBOL(dma_sync_sg_for_device);
 EXPORT_SYMBOL(dmabounce_register_dev);
 EXPORT_SYMBOL(dmabounce_unregister_dev);
 
index 02cede2..dbf68dc 100644 (file)
@@ -42,7 +42,7 @@
 #define GPMC_STATUS            0x54
 #define GPMC_PREFETCH_CONFIG1  0x1e0
 #define GPMC_PREFETCH_CONFIG2  0x1e4
-#define GPMC_PREFETCH_CONTROL  0x1e8
+#define GPMC_PREFETCH_CONTROL  0x1ec
 #define GPMC_PREFETCH_STATUS   0x1f0
 #define GPMC_ECC_CONFIG                0x1f4
 #define GPMC_ECC_CONTROL       0x1f8
index 3e57428..8e813ed 100644 (file)
@@ -74,6 +74,8 @@ static DEFINE_SPINLOCK(boot_lock);
 
 void __cpuinit platform_secondary_init(unsigned int cpu)
 {
+       trace_hardirqs_off();
+
        /*
         * the primary core may have used a "cross call" soft interrupt
         * to get this processor out of WFI in the BootMonitor - make
index c00eda5..39c637b 100644 (file)
@@ -501,8 +501,6 @@ static inline void omap_enable_channel_irq(int lch)
 
        /* Enable some nice interrupts. */
        OMAP_DMA_CICR_REG(lch) = dma_chan[lch].enabled_irqs;
-
-       dma_chan[lch].flags |= OMAP_DMA_ACTIVE;
 }
 
 static void omap_disable_channel_irq(int lch)
index 4ae15c8..632cda8 100644 (file)
@@ -547,7 +547,8 @@ setup_arch (char **cmdline_p)
 # ifdef CONFIG_ACPI_NUMA
        acpi_numa_init();
        per_cpu_scan_finalize((cpus_weight(early_cpu_possible_map) == 0 ?
-               32 : cpus_weight(early_cpu_possible_map)), additional_cpus);
+               32 : cpus_weight(early_cpu_possible_map)),
+               additional_cpus > 0 ? additional_cpus : 0);
 # endif
 #else
 # ifdef CONFIG_SMP
index 8c73643..aad1b7b 100644 (file)
@@ -117,6 +117,7 @@ void account_system_vtime(struct task_struct *tsk)
 
        local_irq_restore(flags);
 }
+EXPORT_SYMBOL_GPL(account_system_vtime);
 
 /*
  * Called from the timer interrupt handler to charge accumulated user time
index 1cee2f9..095e04d 100644 (file)
@@ -273,7 +273,8 @@ endif
 initrd-  := $(patsubst zImage%, zImage.initrd%, $(image-n) $(image-))
 initrd-y := $(patsubst zImage%, zImage.initrd%, \
                $(patsubst dtbImage%, dtbImage.initrd%, \
-               $(patsubst treeImage%, treeImage.initrd%, $(image-y))))
+               $(patsubst simpleImage%, simpleImage.initrd%, \
+               $(patsubst treeImage%, treeImage.initrd%, $(image-y)))))
 initrd-y := $(filter-out $(image-y), $(initrd-y))
 targets        += $(image-y) $(initrd-y)
 
index 61dd174..cf37f5c 100644 (file)
@@ -136,6 +136,11 @@ static int __init add_legacy_soc_port(struct device_node *np,
        if (of_get_property(np, "clock-frequency", NULL) == NULL)
                return -1;
 
+       /* if reg-shift or offset, don't try to use it */
+       if ((of_get_property(np, "reg-shift", NULL) != NULL) ||
+               (of_get_property(np, "reg-offset", NULL) != NULL))
+               return -1;
+
        /* if rtas uses this device, don't try to use it as well */
        if (of_get_property(np, "used-by-rtas", NULL) != NULL)
                return -1;
index 41c7fd9..fe92e65 100644 (file)
@@ -14,6 +14,7 @@ static struct mpc52xx_sdma __iomem *bes;
 static struct mpc52xx_xlb __iomem *xlb;
 static struct mpc52xx_gpio __iomem *gps;
 static struct mpc52xx_gpio_wkup __iomem *gpw;
+static void __iomem *pci;
 static void __iomem *sram;
 static const int sram_size = 0x4000;   /* 16 kBytes */
 static void __iomem *mbar;
@@ -50,6 +51,8 @@ static int lite5200_pm_prepare(void)
                { .type = "builtin", .compatible = "mpc5200", }, /* efika */
                {}
        };
+       u64 regaddr64 = 0;
+       const u32 *regaddr_p;
 
        /* deep sleep? let mpc52xx code handle that */
        if (lite5200_pm_target_state == PM_SUSPEND_STANDBY)
@@ -60,8 +63,12 @@ static int lite5200_pm_prepare(void)
 
        /* map registers */
        np = of_find_matching_node(NULL, immr_ids);
-       mbar = of_iomap(np, 0);
+       regaddr_p = of_get_address(np, 0, NULL, NULL);
+       if (regaddr_p)
+               regaddr64 = of_translate_address(np, regaddr_p);
        of_node_put(np);
+
+       mbar = ioremap((u32) regaddr64, 0xC000);
        if (!mbar) {
                printk(KERN_ERR "%s:%i Error mapping registers\n", __func__, __LINE__);
                return -ENOSYS;
@@ -71,6 +78,7 @@ static int lite5200_pm_prepare(void)
        pic = mbar + 0x500;
        gps = mbar + 0xb00;
        gpw = mbar + 0xc00;
+       pci = mbar + 0xd00;
        bes = mbar + 0x1200;
        xlb = mbar + 0x1f00;
        sram = mbar + 0x8000;
@@ -85,6 +93,7 @@ static struct mpc52xx_sdma sbes;
 static struct mpc52xx_xlb sxlb;
 static struct mpc52xx_gpio sgps;
 static struct mpc52xx_gpio_wkup sgpw;
+static char spci[0x200];
 
 static void lite5200_save_regs(void)
 {
@@ -94,6 +103,7 @@ static void lite5200_save_regs(void)
        _memcpy_fromio(&sxlb, xlb, sizeof(*xlb));
        _memcpy_fromio(&sgps, gps, sizeof(*gps));
        _memcpy_fromio(&sgpw, gpw, sizeof(*gpw));
+       _memcpy_fromio(spci, pci, 0x200);
 
        _memcpy_fromio(saved_sram, sram, sram_size);
 }
@@ -103,6 +113,8 @@ static void lite5200_restore_regs(void)
        int i;
        _memcpy_toio(sram, saved_sram, sram_size);
 
+       /* PCI Configuration */
+       _memcpy_toio(pci, spci, 0x200);
 
        /*
         * GPIOs. Interrupt Master Enable has higher address then other
index e0edaaa..bf07b6f 100644 (file)
@@ -966,8 +966,8 @@ config NUMA_EMU
          number of nodes. This is only useful for debugging.
 
 config NODES_SHIFT
-       int "Max num nodes shift(1-15)"
-       range 1 15  if X86_64
+       int "Max num nodes shift(1-9)"
+       range 1 9  if X86_64
        default "6" if X86_64
        default "4" if X86_NUMAQ
        default "3"
index 8c39467..743f33a 100644 (file)
@@ -831,6 +831,8 @@ static void as_completed_request(struct request_queue *q, struct request *rq)
        }
 
        if (ad->changed_batch && ad->nr_dispatched == 1) {
+               ad->current_batch_expires = jiffies +
+                                       ad->batch_expire[ad->batch_data_dir];
                kblockd_schedule_work(&ad->antic_work);
                ad->changed_batch = 0;
 
index f7f16e7..df03611 100644 (file)
@@ -62,11 +62,11 @@ static void i915_vblank_tasklet(struct drm_device *dev)
        u32 ropcpp = (0xcc << 16) | ((cpp - 1) << 24);
        RING_LOCALS;
 
-       if (sarea_priv->front_tiled) {
+       if (IS_I965G(dev) && sarea_priv->front_tiled) {
                cmd |= XY_SRC_COPY_BLT_DST_TILED;
                dst_pitch >>= 2;
        }
-       if (sarea_priv->back_tiled) {
+       if (IS_I965G(dev) && sarea_priv->back_tiled) {
                cmd |= XY_SRC_COPY_BLT_SRC_TILED;
                src_pitch >>= 2;
        }
index e94bee0..7501310 100644 (file)
@@ -3322,7 +3322,7 @@ static int send_break(struct tty_struct *tty, unsigned int duration)
                msleep_interruptible(duration);
        tty->ops->break_ctl(tty, 0);
        tty_write_unlock(tty);
-       if (!signal_pending(current))
+       if (signal_pending(current))
                return -EINTR;
        return 0;
 }
index 85e2ba7..bf48300 100644 (file)
@@ -27,6 +27,8 @@
 #include <linux/moduleparam.h>
 #include <linux/connector.h>
 #include <linux/mutex.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
 
 #include <net/sock.h>
 
@@ -403,6 +405,40 @@ static void cn_callback(void *data)
        mutex_unlock(&notify_lock);
 }
 
+static int cn_proc_show(struct seq_file *m, void *v)
+{
+       struct cn_queue_dev *dev = cdev.cbdev;
+       struct cn_callback_entry *cbq;
+
+       seq_printf(m, "Name            ID\n");
+
+       spin_lock_bh(&dev->queue_lock);
+
+       list_for_each_entry(cbq, &dev->queue_list, callback_entry) {
+               seq_printf(m, "%-15s %u:%u\n",
+                          cbq->id.name,
+                          cbq->id.id.idx,
+                          cbq->id.id.val);
+       }
+
+       spin_unlock_bh(&dev->queue_lock);
+
+       return 0;
+}
+
+static int cn_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, cn_proc_show, NULL);
+}
+
+static const struct file_operations cn_file_ops = {
+       .owner   = THIS_MODULE,
+       .open    = cn_proc_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = single_release
+};
+
 static int __devinit cn_init(void)
 {
        struct cn_dev *dev = &cdev;
@@ -434,6 +470,8 @@ static int __devinit cn_init(void)
                return -EINVAL;
        }
 
+       proc_net_fops_create(&init_net, "connector", S_IRUGO, &cn_file_ops);
+
        return 0;
 }
 
@@ -443,6 +481,8 @@ static void __devexit cn_fini(void)
 
        cn_already_initialized = 0;
 
+       proc_net_remove(&init_net, "connector");
+
        cn_del_callback(&dev->id);
        cn_queue_free_dev(dev->cbdev);
        netlink_kernel_release(dev->nls);
index 8e07de2..1607536 100644 (file)
@@ -823,6 +823,13 @@ config BLK_DEV_IDE_RAPIDE
          Say Y here if you want to support the Yellowstone RapIDE controller
          manufactured for use with Acorn computers.
 
+config BLK_DEV_IDE_BAST
+       tristate "Simtec BAST / Thorcom VR1000 IDE support"
+       depends on ARM && (ARCH_BAST || MACH_VR1000)
+       help
+         Say Y here if you want to support the onboard IDE channels on the
+         Simtec BAST or the Thorcom VR1000
+
 config IDE_H8300
        tristate "H8300 IDE support"
        depends on H8300
index 5bc2605..936e7b0 100644 (file)
@@ -1,6 +1,7 @@
 
 obj-$(CONFIG_BLK_DEV_IDE_ICSIDE)       += icside.o
 obj-$(CONFIG_BLK_DEV_IDE_RAPIDE)       += rapide.o
+obj-$(CONFIG_BLK_DEV_IDE_BAST)         += bast-ide.o
 obj-$(CONFIG_BLK_DEV_PALMCHIP_BK3710)  += palm_bk3710.o
 
 ifeq ($(CONFIG_IDE_ARM), m)
diff --git a/drivers/ide/arm/bast-ide.c b/drivers/ide/arm/bast-ide.c
new file mode 100644 (file)
index 0000000..8e8c281
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2003-2004 Simtec Electronics
+ *  Ben Dooks <ben@simtec.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+*/
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/ide.h>
+#include <linux/init.h>
+
+#include <asm/mach-types.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/arch/map.h>
+#include <asm/arch/bast-map.h>
+#include <asm/arch/bast-irq.h>
+
+#define DRV_NAME "bast-ide"
+
+static int __init bastide_register(unsigned int base, unsigned int aux, int irq)
+{
+       ide_hwif_t *hwif;
+       hw_regs_t hw;
+       int i;
+       u8 idx[4] = { 0xff, 0xff, 0xff, 0xff };
+
+       memset(&hw, 0, sizeof(hw));
+
+       base += BAST_IDE_CS;
+       aux  += BAST_IDE_CS;
+
+       for (i = 0; i <= 7; i++) {
+               hw.io_ports_array[i] = (unsigned long)base;
+               base += 0x20;
+       }
+
+       hw.io_ports.ctl_addr = aux + (6 * 0x20);
+       hw.irq = irq;
+       hw.chipset = ide_generic;
+
+       hwif = ide_find_port();
+       if (hwif == NULL)
+               goto out;
+
+       i = hwif->index;
+
+       ide_init_port_data(hwif, i);
+       ide_init_port_hw(hwif, &hw);
+       hwif->port_ops = NULL;
+
+       idx[0] = i;
+
+       ide_device_add(idx, NULL);
+out:
+       return 0;
+}
+
+static int __init bastide_init(void)
+{
+       unsigned long base = BAST_VA_IDEPRI + BAST_IDE_CS;
+
+       /* we can treat the VR1000 and the BAST the same */
+
+       if (!(machine_is_bast() || machine_is_vr1000()))
+               return 0;
+
+       printk("BAST: IDE driver, (c) 2003-2004 Simtec Electronics\n");
+
+       if (!request_mem_region(base, 0x400000, DRV_NAME)) {
+               printk(KERN_ERR "%s: resources busy\n", DRV_NAME);
+               return -EBUSY;
+       }
+
+       bastide_register(BAST_VA_IDEPRI, BAST_VA_IDEPRIAUX, IRQ_IDE0);
+       bastide_register(BAST_VA_IDESEC, BAST_VA_IDESECAUX, IRQ_IDE1);
+
+       return 0;
+}
+
+module_init(bastide_init);
+
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Simtec BAST / Thorcom VR1000 IDE driver");
index 55ec7f7..8af88bf 100644 (file)
@@ -76,7 +76,7 @@ static int proc_ide_read_mate
        ide_hwif_t      *hwif = (ide_hwif_t *) data;
        int             len;
 
-       if (hwif && hwif->mate && hwif->mate->present)
+       if (hwif && hwif->mate)
                len = sprintf(page, "%s\n", hwif->mate->name);
        else
                len = sprintf(page, "(none)\n");
index 4c01464..72c63e5 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/input.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/sched.h>
 
 /*
  * Check that the effect_id is a valid effect and whether the user
index 835def1..ab6a61d 100644 (file)
@@ -432,6 +432,7 @@ static int crypt_convert(struct crypt_config *cc,
                case 0:
                        atomic_dec(&ctx->pending);
                        ctx->sector++;
+                       cond_resched();
                        continue;
 
                /* error */
index 7cf512a..2580ac1 100644 (file)
@@ -3897,8 +3897,10 @@ static void autorun_devices(int part)
 
                md_probe(dev, NULL, NULL);
                mddev = mddev_find(dev);
-               if (!mddev) {
-                       printk(KERN_ERR 
+               if (!mddev || !mddev->gendisk) {
+                       if (mddev)
+                               mddev_put(mddev);
+                       printk(KERN_ERR
                                "md: cannot allocate memory for md drive.\n");
                        break;
                }
index 1de17da..a71277b 100644 (file)
@@ -2137,6 +2137,8 @@ static int run(mddev_t *mddev)
                    !test_bit(In_sync, &disk->rdev->flags)) {
                        disk->head_position = 0;
                        mddev->degraded++;
+                       if (disk->rdev)
+                               conf->fullsync = 1;
                }
        }
 
index c37e256..54c8ee2 100644 (file)
@@ -2898,6 +2898,8 @@ static void handle_stripe5(struct stripe_head *sh)
 
                for (i = conf->raid_disks; i--; ) {
                        set_bit(R5_Wantwrite, &sh->dev[i].flags);
+                       set_bit(R5_LOCKED, &dev->flags);
+                       s.locked++;
                        if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending))
                                sh->ops.count++;
                }
@@ -2911,6 +2913,7 @@ static void handle_stripe5(struct stripe_head *sh)
                        conf->raid_disks);
                s.locked += handle_write_operations5(sh, 1, 1);
        } else if (s.expanded &&
+                  s.locked == 0 &&
                !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending)) {
                clear_bit(STRIPE_EXPAND_READY, &sh->state);
                atomic_dec(&conf->reshape_stripes);
@@ -4305,7 +4308,9 @@ static int run(mddev_t *mddev)
                                " disk %d\n", bdevname(rdev->bdev,b),
                                raid_disk);
                        working_disks++;
-               }
+               } else
+                       /* Cannot rely on bitmap to complete recovery */
+                       conf->fullsync = 1;
        }
 
        /*
index 3b26fbd..5ccb0ae 100644 (file)
@@ -793,6 +793,14 @@ menuconfig V4L_USB_DRIVERS
 
 if V4L_USB_DRIVERS && USB
 
+config USB_VIDEO_CLASS
+       tristate "USB Video Class (UVC)"
+       ---help---
+         Support for the USB Video Class (UVC).  Currently only video
+         input devices, such as webcams, are supported.
+
+         For more information see: <http://linux-uvc.berlios.de/>
+
 source "drivers/media/video/pvrusb2/Kconfig"
 
 source "drivers/media/video/em28xx/Kconfig"
index dff0d6a..ecbbfaa 100644 (file)
@@ -136,6 +136,8 @@ obj-$(CONFIG_SOC_CAMERA_MT9V022)    += mt9v022.o
 
 obj-$(CONFIG_VIDEO_AU0828) += au0828/
 
+obj-$(CONFIG_USB_VIDEO_CLASS)  += uvc/
+
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
 EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
 EXTRA_CFLAGS += -Idrivers/media/common/tuners
diff --git a/drivers/media/video/uvc/Makefile b/drivers/media/video/uvc/Makefile
new file mode 100644 (file)
index 0000000..968c199
--- /dev/null
@@ -0,0 +1,3 @@
+uvcvideo-objs  := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \
+                 uvc_status.o uvc_isight.o
+obj-$(CONFIG_USB_VIDEO_CLASS) += uvcvideo.o
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
new file mode 100644 (file)
index 0000000..f0ee46d
--- /dev/null
@@ -0,0 +1,1256 @@
+/*
+ *      uvc_ctrl.c  --  USB Video Class driver - Controls
+ *
+ *      Copyright (C) 2005-2008
+ *          Laurent Pinchart (laurent.pinchart@skynet.be)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+
+#include "uvcvideo.h"
+
+#define UVC_CTRL_NDATA         2
+#define UVC_CTRL_DATA_CURRENT  0
+#define UVC_CTRL_DATA_BACKUP   1
+
+/* ------------------------------------------------------------------------
+ * Control, formats, ...
+ */
+
+static struct uvc_control_info uvc_ctrls[] = {
+       {
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_BRIGHTNESS_CONTROL,
+               .index          = 0,
+               .size           = 2,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+                               | UVC_CONTROL_RESTORE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_CONTRAST_CONTROL,
+               .index          = 1,
+               .size           = 2,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+                               | UVC_CONTROL_RESTORE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_HUE_CONTROL,
+               .index          = 2,
+               .size           = 2,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+                               | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_SATURATION_CONTROL,
+               .index          = 3,
+               .size           = 2,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+                               | UVC_CONTROL_RESTORE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_SHARPNESS_CONTROL,
+               .index          = 4,
+               .size           = 2,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+                               | UVC_CONTROL_RESTORE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_GAMMA_CONTROL,
+               .index          = 5,
+               .size           = 2,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+                               | UVC_CONTROL_RESTORE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_BACKLIGHT_COMPENSATION_CONTROL,
+               .index          = 8,
+               .size           = 2,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+                               | UVC_CONTROL_RESTORE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_GAIN_CONTROL,
+               .index          = 9,
+               .size           = 2,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+                               | UVC_CONTROL_RESTORE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_POWER_LINE_FREQUENCY_CONTROL,
+               .index          = 10,
+               .size           = 1,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+                               | UVC_CONTROL_RESTORE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_HUE_AUTO_CONTROL,
+               .index          = 11,
+               .size           = 1,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+                               | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_CAMERA,
+               .selector       = CT_AE_MODE_CONTROL,
+               .index          = 1,
+               .size           = 1,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+                               | UVC_CONTROL_GET_DEF | UVC_CONTROL_GET_RES
+                               | UVC_CONTROL_RESTORE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_CAMERA,
+               .selector       = CT_AE_PRIORITY_CONTROL,
+               .index          = 2,
+               .size           = 1,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+                               | UVC_CONTROL_RESTORE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_CAMERA,
+               .selector       = CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
+               .index          = 3,
+               .size           = 4,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+                               | UVC_CONTROL_RESTORE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_CAMERA,
+               .selector       = CT_FOCUS_ABSOLUTE_CONTROL,
+               .index          = 5,
+               .size           = 2,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+                               | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_CAMERA,
+               .selector       = CT_FOCUS_AUTO_CONTROL,
+               .index          = 17,
+               .size           = 1,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+                               | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
+               .index          = 12,
+               .size           = 1,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+                               | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
+               .index          = 6,
+               .size           = 2,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+                               | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
+               .index          = 13,
+               .size           = 1,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
+                               | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE,
+       },
+       {
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_WHITE_BALANCE_COMPONENT_CONTROL,
+               .index          = 7,
+               .size           = 4,
+               .flags          = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE
+                               | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE,
+       },
+};
+
+static struct uvc_menu_info power_line_frequency_controls[] = {
+       { 0, "Disabled" },
+       { 1, "50 Hz" },
+       { 2, "60 Hz" },
+};
+
+static struct uvc_menu_info exposure_auto_controls[] = {
+       { 1, "Manual Mode" },
+       { 2, "Auto Mode" },
+       { 4, "Shutter Priority Mode" },
+       { 8, "Aperture Priority Mode" },
+};
+
+static struct uvc_control_mapping uvc_ctrl_mappings[] = {
+       {
+               .id             = V4L2_CID_BRIGHTNESS,
+               .name           = "Brightness",
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_BRIGHTNESS_CONTROL,
+               .size           = 16,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
+       },
+       {
+               .id             = V4L2_CID_CONTRAST,
+               .name           = "Contrast",
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_CONTRAST_CONTROL,
+               .size           = 16,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
+       },
+       {
+               .id             = V4L2_CID_HUE,
+               .name           = "Hue",
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_HUE_CONTROL,
+               .size           = 16,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
+       },
+       {
+               .id             = V4L2_CID_SATURATION,
+               .name           = "Saturation",
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_SATURATION_CONTROL,
+               .size           = 16,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
+       },
+       {
+               .id             = V4L2_CID_SHARPNESS,
+               .name           = "Sharpness",
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_SHARPNESS_CONTROL,
+               .size           = 16,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
+       },
+       {
+               .id             = V4L2_CID_GAMMA,
+               .name           = "Gamma",
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_GAMMA_CONTROL,
+               .size           = 16,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
+       },
+       {
+               .id             = V4L2_CID_BACKLIGHT_COMPENSATION,
+               .name           = "Backlight Compensation",
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_BACKLIGHT_COMPENSATION_CONTROL,
+               .size           = 16,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
+       },
+       {
+               .id             = V4L2_CID_GAIN,
+               .name           = "Gain",
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_GAIN_CONTROL,
+               .size           = 16,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
+       },
+       {
+               .id             = V4L2_CID_POWER_LINE_FREQUENCY,
+               .name           = "Power Line Frequency",
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_POWER_LINE_FREQUENCY_CONTROL,
+               .size           = 2,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_MENU,
+               .data_type      = UVC_CTRL_DATA_TYPE_ENUM,
+               .menu_info      = power_line_frequency_controls,
+               .menu_count     = ARRAY_SIZE(power_line_frequency_controls),
+       },
+       {
+               .id             = V4L2_CID_HUE_AUTO,
+               .name           = "Hue, Auto",
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_HUE_AUTO_CONTROL,
+               .size           = 1,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
+               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
+       },
+       {
+               .id             = V4L2_CID_EXPOSURE_AUTO,
+               .name           = "Exposure, Auto",
+               .entity         = UVC_GUID_UVC_CAMERA,
+               .selector       = CT_AE_MODE_CONTROL,
+               .size           = 4,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_MENU,
+               .data_type      = UVC_CTRL_DATA_TYPE_BITMASK,
+               .menu_info      = exposure_auto_controls,
+               .menu_count     = ARRAY_SIZE(exposure_auto_controls),
+       },
+       {
+               .id             = V4L2_CID_EXPOSURE_AUTO_PRIORITY,
+               .name           = "Exposure, Auto Priority",
+               .entity         = UVC_GUID_UVC_CAMERA,
+               .selector       = CT_AE_PRIORITY_CONTROL,
+               .size           = 1,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
+               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
+       },
+       {
+               .id             = V4L2_CID_EXPOSURE_ABSOLUTE,
+               .name           = "Exposure (Absolute)",
+               .entity         = UVC_GUID_UVC_CAMERA,
+               .selector       = CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
+               .size           = 32,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
+       },
+       {
+               .id             = V4L2_CID_AUTO_WHITE_BALANCE,
+               .name           = "White Balance Temperature, Auto",
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
+               .size           = 1,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
+               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
+       },
+       {
+               .id             = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+               .name           = "White Balance Temperature",
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
+               .size           = 16,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
+       },
+       {
+               .id             = V4L2_CID_AUTO_WHITE_BALANCE,
+               .name           = "White Balance Component, Auto",
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL,
+               .size           = 1,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
+               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
+       },
+       {
+               .id             = V4L2_CID_BLUE_BALANCE,
+               .name           = "White Balance Blue Component",
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_WHITE_BALANCE_COMPONENT_CONTROL,
+               .size           = 16,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
+       },
+       {
+               .id             = V4L2_CID_RED_BALANCE,
+               .name           = "White Balance Red Component",
+               .entity         = UVC_GUID_UVC_PROCESSING,
+               .selector       = PU_WHITE_BALANCE_COMPONENT_CONTROL,
+               .size           = 16,
+               .offset         = 16,
+               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+               .data_type      = UVC_CTRL_DATA_TYPE_SIGNED,
+       },
+       {
+               .id             = V4L2_CID_FOCUS_ABSOLUTE,
+               .name           = "Focus (absolute)",
+               .entity         = UVC_GUID_UVC_CAMERA,
+               .selector       = CT_FOCUS_ABSOLUTE_CONTROL,
+               .size           = 16,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_INTEGER,
+               .data_type      = UVC_CTRL_DATA_TYPE_UNSIGNED,
+       },
+       {
+               .id             = V4L2_CID_FOCUS_AUTO,
+               .name           = "Focus, Auto",
+               .entity         = UVC_GUID_UVC_CAMERA,
+               .selector       = CT_FOCUS_AUTO_CONTROL,
+               .size           = 1,
+               .offset         = 0,
+               .v4l2_type      = V4L2_CTRL_TYPE_BOOLEAN,
+               .data_type      = UVC_CTRL_DATA_TYPE_BOOLEAN,
+       },
+};
+
+/* ------------------------------------------------------------------------
+ * Utility functions
+ */
+
+static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
+{
+       return ctrl->data + id * ctrl->info->size;
+}
+
+static inline int uvc_get_bit(const __u8 *data, int bit)
+{
+       return (data[bit >> 3] >> (bit & 7)) & 1;
+}
+
+/* Extract the bit string specified by mapping->offset and mapping->size
+ * from the little-endian data stored at 'data' and return the result as
+ * a signed 32bit integer. Sign extension will be performed if the mapping
+ * references a signed data type.
+ */
+static __s32 uvc_get_le_value(const __u8 *data,
+       struct uvc_control_mapping *mapping)
+{
+       int bits = mapping->size;
+       int offset = mapping->offset;
+       __s32 value = 0;
+       __u8 mask;
+
+       data += offset / 8;
+       offset &= 7;
+       mask = ((1LL << bits) - 1) << offset;
+
+       for (; bits > 0; data++) {
+               __u8 byte = *data & mask;
+               value |= offset > 0 ? (byte >> offset) : (byte << (-offset));
+               bits -= 8 - (offset > 0 ? offset : 0);
+               offset -= 8;
+               mask = (1 << bits) - 1;
+       }
+
+       /* Sign-extend the value if needed */
+       if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED)
+               value |= -(value & (1 << (mapping->size - 1)));
+
+       return value;
+}
+
+/* Set the bit string specified by mapping->offset and mapping->size
+ * in the little-endian data stored at 'data' to the value 'value'.
+ */
+static void uvc_set_le_value(__s32 value, __u8 *data,
+       struct uvc_control_mapping *mapping)
+{
+       int bits = mapping->size;
+       int offset = mapping->offset;
+       __u8 mask;
+
+       data += offset / 8;
+       offset &= 7;
+
+       for (; bits > 0; data++) {
+               mask = ((1LL << bits) - 1) << offset;
+               *data = (*data & ~mask) | ((value << offset) & mask);
+               value >>= offset ? offset : 8;
+               bits -= 8 - offset;
+               offset = 0;
+       }
+}
+
+/* ------------------------------------------------------------------------
+ * Terminal and unit management
+ */
+
+static const __u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING;
+static const __u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA;
+static const __u8 uvc_media_transport_input_guid[16] =
+       UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT;
+
+static int uvc_entity_match_guid(struct uvc_entity *entity, __u8 guid[16])
+{
+       switch (UVC_ENTITY_TYPE(entity)) {
+       case ITT_CAMERA:
+               return memcmp(uvc_camera_guid, guid, 16) == 0;
+
+       case ITT_MEDIA_TRANSPORT_INPUT:
+               return memcmp(uvc_media_transport_input_guid, guid, 16) == 0;
+
+       case VC_PROCESSING_UNIT:
+               return memcmp(uvc_processing_guid, guid, 16) == 0;
+
+       case VC_EXTENSION_UNIT:
+               return memcmp(entity->extension.guidExtensionCode,
+                             guid, 16) == 0;
+
+       default:
+               return 0;
+       }
+}
+
+/* ------------------------------------------------------------------------
+ * UVC Controls
+ */
+
+static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id,
+       struct uvc_control_mapping **mapping, struct uvc_control **control,
+       int next)
+{
+       struct uvc_control *ctrl;
+       struct uvc_control_mapping *map;
+       unsigned int i;
+
+       if (entity == NULL)
+               return;
+
+       for (i = 0; i < entity->ncontrols; ++i) {
+               ctrl = &entity->controls[i];
+               if (ctrl->info == NULL)
+                       continue;
+
+               list_for_each_entry(map, &ctrl->info->mappings, list) {
+                       if ((map->id == v4l2_id) && !next) {
+                               *control = ctrl;
+                               *mapping = map;
+                               return;
+                       }
+
+                       if ((*mapping == NULL || (*mapping)->id > map->id) &&
+                           (map->id > v4l2_id) && next) {
+                               *control = ctrl;
+                               *mapping = map;
+                       }
+               }
+       }
+}
+
+struct uvc_control *uvc_find_control(struct uvc_video_device *video,
+       __u32 v4l2_id, struct uvc_control_mapping **mapping)
+{
+       struct uvc_control *ctrl = NULL;
+       struct uvc_entity *entity;
+       int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL;
+
+       *mapping = NULL;
+
+       /* Mask the query flags. */
+       v4l2_id &= V4L2_CTRL_ID_MASK;
+
+       /* Find the control. */
+       __uvc_find_control(video->processing, v4l2_id, mapping, &ctrl, next);
+       if (ctrl && !next)
+               return ctrl;
+
+       list_for_each_entry(entity, &video->iterms, chain) {
+               __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
+               if (ctrl && !next)
+                       return ctrl;
+       }
+
+       list_for_each_entry(entity, &video->extensions, chain) {
+               __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next);
+               if (ctrl && !next)
+                       return ctrl;
+       }
+
+       if (ctrl == NULL && !next)
+               uvc_trace(UVC_TRACE_CONTROL, "Control 0x%08x not found.\n",
+                               v4l2_id);
+
+       return ctrl;
+}
+
+int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
+       struct v4l2_queryctrl *v4l2_ctrl)
+{
+       struct uvc_control *ctrl;
+       struct uvc_control_mapping *mapping;
+       struct uvc_menu_info *menu;
+       unsigned int i;
+       __u8 data[8];
+       int ret;
+
+       ctrl = uvc_find_control(video, v4l2_ctrl->id, &mapping);
+       if (ctrl == NULL)
+               return -EINVAL;
+
+       v4l2_ctrl->id = mapping->id;
+       v4l2_ctrl->type = mapping->v4l2_type;
+       strncpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name);
+       v4l2_ctrl->flags = 0;
+
+       if (!(ctrl->info->flags & UVC_CONTROL_SET_CUR))
+               v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+       if (ctrl->info->flags & UVC_CONTROL_GET_DEF) {
+               if ((ret = uvc_query_ctrl(video->dev, GET_DEF, ctrl->entity->id,
+                               video->dev->intfnum, ctrl->info->selector,
+                               &data, ctrl->info->size)) < 0)
+                       return ret;
+               v4l2_ctrl->default_value = uvc_get_le_value(data, mapping);
+       }
+
+       if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
+               v4l2_ctrl->minimum = 0;
+               v4l2_ctrl->maximum = mapping->menu_count - 1;
+               v4l2_ctrl->step = 1;
+
+               menu = mapping->menu_info;
+               for (i = 0; i < mapping->menu_count; ++i, ++menu) {
+                       if (menu->value == v4l2_ctrl->default_value) {
+                               v4l2_ctrl->default_value = i;
+                               break;
+                       }
+               }
+
+               return 0;
+       }
+
+       if (ctrl->info->flags & UVC_CONTROL_GET_MIN) {
+               if ((ret = uvc_query_ctrl(video->dev, GET_MIN, ctrl->entity->id,
+                               video->dev->intfnum, ctrl->info->selector,
+                               &data, ctrl->info->size)) < 0)
+                       return ret;
+               v4l2_ctrl->minimum = uvc_get_le_value(data, mapping);
+       }
+       if (ctrl->info->flags & UVC_CONTROL_GET_MAX) {
+               if ((ret = uvc_query_ctrl(video->dev, GET_MAX, ctrl->entity->id,
+                               video->dev->intfnum, ctrl->info->selector,
+                               &data, ctrl->info->size)) < 0)
+                       return ret;
+               v4l2_ctrl->maximum = uvc_get_le_value(data, mapping);
+       }
+       if (ctrl->info->flags & UVC_CONTROL_GET_RES) {
+               if ((ret = uvc_query_ctrl(video->dev, GET_RES, ctrl->entity->id,
+                               video->dev->intfnum, ctrl->info->selector,
+                               &data, ctrl->info->size)) < 0)
+                       return ret;
+               v4l2_ctrl->step = uvc_get_le_value(data, mapping);
+       }
+
+       return 0;
+}
+
+
+/* --------------------------------------------------------------------------
+ * Control transactions
+ *
+ * To make extended set operations as atomic as the hardware allows, controls
+ * are handled using begin/commit/rollback operations.
+ *
+ * At the beginning of a set request, uvc_ctrl_begin should be called to
+ * initialize the request. This function acquires the control lock.
+ *
+ * When setting a control, the new value is stored in the control data field
+ * at position UVC_CTRL_DATA_CURRENT. The control is then marked as dirty for
+ * later processing. If the UVC and V4L2 control sizes differ, the current
+ * value is loaded from the hardware before storing the new value in the data
+ * field.
+ *
+ * After processing all controls in the transaction, uvc_ctrl_commit or
+ * uvc_ctrl_rollback must be called to apply the pending changes to the
+ * hardware or revert them. When applying changes, all controls marked as
+ * dirty will be modified in the UVC device, and the dirty flag will be
+ * cleared. When reverting controls, the control data field
+ * UVC_CTRL_DATA_CURRENT is reverted to its previous value
+ * (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the
+ * control lock.
+ */
+int uvc_ctrl_begin(struct uvc_video_device *video)
+{
+       return mutex_lock_interruptible(&video->ctrl_mutex) ? -ERESTARTSYS : 0;
+}
+
+static int uvc_ctrl_commit_entity(struct uvc_device *dev,
+       struct uvc_entity *entity, int rollback)
+{
+       struct uvc_control *ctrl;
+       unsigned int i;
+       int ret;
+
+       if (entity == NULL)
+               return 0;
+
+       for (i = 0; i < entity->ncontrols; ++i) {
+               ctrl = &entity->controls[i];
+               if (ctrl->info == NULL || !ctrl->dirty)
+                       continue;
+
+               if (!rollback)
+                       ret = uvc_query_ctrl(dev, SET_CUR, ctrl->entity->id,
+                               dev->intfnum, ctrl->info->selector,
+                               uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+                               ctrl->info->size);
+               else
+                       ret = 0;
+
+               if (rollback || ret < 0)
+                       memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+                              uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
+                              ctrl->info->size);
+
+               if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0)
+                       ctrl->loaded = 0;
+
+               ctrl->dirty = 0;
+
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback)
+{
+       struct uvc_entity *entity;
+       int ret = 0;
+
+       /* Find the control. */
+       ret = uvc_ctrl_commit_entity(video->dev, video->processing, rollback);
+       if (ret < 0)
+               goto done;
+
+       list_for_each_entry(entity, &video->iterms, chain) {
+               ret = uvc_ctrl_commit_entity(video->dev, entity, rollback);
+               if (ret < 0)
+                       goto done;
+       }
+
+       list_for_each_entry(entity, &video->extensions, chain) {
+               ret = uvc_ctrl_commit_entity(video->dev, entity, rollback);
+               if (ret < 0)
+                       goto done;
+       }
+
+done:
+       mutex_unlock(&video->ctrl_mutex);
+       return ret;
+}
+
+int uvc_ctrl_get(struct uvc_video_device *video,
+       struct v4l2_ext_control *xctrl)
+{
+       struct uvc_control *ctrl;
+       struct uvc_control_mapping *mapping;
+       struct uvc_menu_info *menu;
+       unsigned int i;
+       int ret;
+
+       ctrl = uvc_find_control(video, xctrl->id, &mapping);
+       if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0)
+               return -EINVAL;
+
+       if (!ctrl->loaded) {
+               ret = uvc_query_ctrl(video->dev, GET_CUR, ctrl->entity->id,
+                               video->dev->intfnum, ctrl->info->selector,
+                               uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+                               ctrl->info->size);
+               if (ret < 0)
+                       return ret;
+
+               if ((ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE) == 0)
+                       ctrl->loaded = 1;
+       }
+
+       xctrl->value = uvc_get_le_value(
+               uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping);
+
+       if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
+               menu = mapping->menu_info;
+               for (i = 0; i < mapping->menu_count; ++i, ++menu) {
+                       if (menu->value == xctrl->value) {
+                               xctrl->value = i;
+                               break;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+int uvc_ctrl_set(struct uvc_video_device *video,
+       struct v4l2_ext_control *xctrl)
+{
+       struct uvc_control *ctrl;
+       struct uvc_control_mapping *mapping;
+       s32 value = xctrl->value;
+       int ret;
+
+       ctrl = uvc_find_control(video, xctrl->id, &mapping);
+       if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0)
+               return -EINVAL;
+
+       if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
+               if (value < 0 || value >= mapping->menu_count)
+                       return -EINVAL;
+               value = mapping->menu_info[value].value;
+       }
+
+       if (!ctrl->loaded && (ctrl->info->size * 8) != mapping->size) {
+               if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) {
+                       memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+                               0, ctrl->info->size);
+               } else {
+                       ret = uvc_query_ctrl(video->dev, GET_CUR,
+                               ctrl->entity->id, video->dev->intfnum,
+                               ctrl->info->selector,
+                               uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+                               ctrl->info->size);
+                       if (ret < 0)
+                               return ret;
+               }
+
+               if ((ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE) == 0)
+                       ctrl->loaded = 1;
+       }
+
+       if (!ctrl->dirty) {
+               memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
+                      uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+                      ctrl->info->size);
+       }
+
+       uvc_set_le_value(value,
+               uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping);
+
+       ctrl->dirty = 1;
+       ctrl->modified = 1;
+       return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * Dynamic controls
+ */
+
+int uvc_xu_ctrl_query(struct uvc_video_device *video,
+       struct uvc_xu_control *xctrl, int set)
+{
+       struct uvc_entity *entity;
+       struct uvc_control *ctrl = NULL;
+       unsigned int i, found = 0;
+       __u8 *data;
+       int ret;
+
+       /* Find the extension unit. */
+       list_for_each_entry(entity, &video->extensions, chain) {
+               if (entity->id == xctrl->unit)
+                       break;
+       }
+
+       if (entity->id != xctrl->unit) {
+               uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n",
+                       xctrl->unit);
+               return -EINVAL;
+       }
+
+       /* Find the control. */
+       for (i = 0; i < entity->ncontrols; ++i) {
+               ctrl = &entity->controls[i];
+               if (ctrl->info == NULL)
+                       continue;
+
+               if (ctrl->info->selector == xctrl->selector) {
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (!found) {
+               uvc_trace(UVC_TRACE_CONTROL,
+                       "Control " UVC_GUID_FORMAT "/%u not found.\n",
+                       UVC_GUID_ARGS(entity->extension.guidExtensionCode),
+                       xctrl->selector);
+               return -EINVAL;
+       }
+
+       /* Validate control data size. */
+       if (ctrl->info->size != xctrl->size)
+               return -EINVAL;
+
+       if ((set && !(ctrl->info->flags & UVC_CONTROL_SET_CUR)) ||
+           (!set && !(ctrl->info->flags & UVC_CONTROL_GET_CUR)))
+               return -EINVAL;
+
+       if (mutex_lock_interruptible(&video->ctrl_mutex))
+               return -ERESTARTSYS;
+
+       memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
+              uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+              xctrl->size);
+       data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT);
+
+       if (set && copy_from_user(data, xctrl->data, xctrl->size)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       ret = uvc_query_ctrl(video->dev, set ? SET_CUR : GET_CUR, xctrl->unit,
+                            video->dev->intfnum, xctrl->selector, data,
+                            xctrl->size);
+       if (ret < 0)
+               goto out;
+
+       if (!set && copy_to_user(xctrl->data, data, xctrl->size)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+out:
+       if (ret)
+               memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+                      uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP),
+                      xctrl->size);
+
+       mutex_unlock(&video->ctrl_mutex);
+       return ret;
+}
+
+/* --------------------------------------------------------------------------
+ * Suspend/resume
+ */
+
+/*
+ * Restore control values after resume, skipping controls that haven't been
+ * changed.
+ *
+ * TODO
+ * - Don't restore modified controls that are back to their default value.
+ * - Handle restore order (Auto-Exposure Mode should be restored before
+ *   Exposure Time).
+ */
+int uvc_ctrl_resume_device(struct uvc_device *dev)
+{
+       struct uvc_control *ctrl;
+       struct uvc_entity *entity;
+       unsigned int i;
+       int ret;
+
+       /* Walk the entities list and restore controls when possible. */
+       list_for_each_entry(entity, &dev->entities, list) {
+
+               for (i = 0; i < entity->ncontrols; ++i) {
+                       ctrl = &entity->controls[i];
+
+                       if (ctrl->info == NULL || !ctrl->modified ||
+                           (ctrl->info->flags & UVC_CONTROL_RESTORE) == 0)
+                               continue;
+
+                       printk(KERN_INFO "restoring control " UVC_GUID_FORMAT
+                               "/%u/%u\n", UVC_GUID_ARGS(ctrl->info->entity),
+                               ctrl->info->index, ctrl->info->selector);
+                       ctrl->dirty = 1;
+               }
+
+               ret = uvc_ctrl_commit_entity(dev, entity, 0);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * Control and mapping handling
+ */
+
+static void uvc_ctrl_add_ctrl(struct uvc_device *dev,
+       struct uvc_control_info *info)
+{
+       struct uvc_entity *entity;
+       struct uvc_control *ctrl = NULL;
+       int ret, found = 0;
+       unsigned int i;
+
+       list_for_each_entry(entity, &dev->entities, list) {
+               if (!uvc_entity_match_guid(entity, info->entity))
+                       continue;
+
+               for (i = 0; i < entity->ncontrols; ++i) {
+                       ctrl = &entity->controls[i];
+                       if (ctrl->index == info->index) {
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (found)
+                       break;
+       }
+
+       if (!found)
+               return;
+
+       if (UVC_ENTITY_TYPE(entity) == VC_EXTENSION_UNIT) {
+               /* Check if the device control information and length match
+                * the user supplied information.
+                */
+               __u32 flags;
+               __le16 size;
+               __u8 inf;
+
+               if ((ret = uvc_query_ctrl(dev, GET_LEN, ctrl->entity->id,
+                       dev->intfnum, info->selector, (__u8 *)&size, 2)) < 0) {
+                       uvc_trace(UVC_TRACE_CONTROL, "GET_LEN failed on "
+                               "control " UVC_GUID_FORMAT "/%u (%d).\n",
+                               UVC_GUID_ARGS(info->entity), info->selector,
+                               ret);
+                       return;
+               }
+
+               if (info->size != le16_to_cpu(size)) {
+                       uvc_trace(UVC_TRACE_CONTROL, "Control " UVC_GUID_FORMAT
+                               "/%u size doesn't match user supplied "
+                               "value.\n", UVC_GUID_ARGS(info->entity),
+                               info->selector);
+                       return;
+               }
+
+               if ((ret = uvc_query_ctrl(dev, GET_INFO, ctrl->entity->id,
+                       dev->intfnum, info->selector, &inf, 1)) < 0) {
+                       uvc_trace(UVC_TRACE_CONTROL, "GET_INFO failed on "
+                               "control " UVC_GUID_FORMAT "/%u (%d).\n",
+                               UVC_GUID_ARGS(info->entity), info->selector,
+                               ret);
+                       return;
+               }
+
+               flags = info->flags;
+               if (((flags & UVC_CONTROL_GET_CUR) && !(inf & (1 << 0))) ||
+                   ((flags & UVC_CONTROL_SET_CUR) && !(inf & (1 << 1)))) {
+                       uvc_trace(UVC_TRACE_CONTROL, "Control "
+                               UVC_GUID_FORMAT "/%u flags don't match "
+                               "supported operations.\n",
+                               UVC_GUID_ARGS(info->entity), info->selector);
+                       return;
+               }
+       }
+
+       ctrl->info = info;
+       ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_NDATA, GFP_KERNEL);
+       uvc_trace(UVC_TRACE_CONTROL, "Added control " UVC_GUID_FORMAT "/%u "
+               "to device %s entity %u\n", UVC_GUID_ARGS(ctrl->info->entity),
+               ctrl->info->selector, dev->udev->devpath, entity->id);
+}
+
+/*
+ * Add an item to the UVC control information list, and instantiate a control
+ * structure for each device that supports the control.
+ */
+int uvc_ctrl_add_info(struct uvc_control_info *info)
+{
+       struct uvc_control_info *ctrl;
+       struct uvc_device *dev;
+       int ret = 0;
+
+       /* Find matching controls by walking the devices, entities and
+        * controls list.
+        */
+       mutex_lock(&uvc_driver.ctrl_mutex);
+
+       /* First check if the list contains a control matching the new one.
+        * Bail out if it does.
+        */
+       list_for_each_entry(ctrl, &uvc_driver.controls, list) {
+               if (memcmp(ctrl->entity, info->entity, 16))
+                       continue;
+
+               if (ctrl->selector == info->selector) {
+                       uvc_trace(UVC_TRACE_CONTROL, "Control "
+                               UVC_GUID_FORMAT "/%u is already defined.\n",
+                               UVC_GUID_ARGS(info->entity), info->selector);
+                       ret = -EEXIST;
+                       goto end;
+               }
+               if (ctrl->index == info->index) {
+                       uvc_trace(UVC_TRACE_CONTROL, "Control "
+                               UVC_GUID_FORMAT "/%u would overwrite index "
+                               "%d.\n", UVC_GUID_ARGS(info->entity),
+                               info->selector, info->index);
+                       ret = -EEXIST;
+                       goto end;
+               }
+       }
+
+       list_for_each_entry(dev, &uvc_driver.devices, list)
+               uvc_ctrl_add_ctrl(dev, info);
+
+       INIT_LIST_HEAD(&info->mappings);
+       list_add_tail(&info->list, &uvc_driver.controls);
+end:
+       mutex_unlock(&uvc_driver.ctrl_mutex);
+       return ret;
+}
+
+int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping)
+{
+       struct uvc_control_info *info;
+       struct uvc_control_mapping *map;
+       int ret = -EINVAL;
+
+       if (mapping->id & ~V4L2_CTRL_ID_MASK) {
+               uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s' with "
+                       "invalid control id 0x%08x\n", mapping->name,
+                       mapping->id);
+               return -EINVAL;
+       }
+
+       mutex_lock(&uvc_driver.ctrl_mutex);
+       list_for_each_entry(info, &uvc_driver.controls, list) {
+               if (memcmp(info->entity, mapping->entity, 16) ||
+                       info->selector != mapping->selector)
+                       continue;
+
+               if (info->size * 8 < mapping->size + mapping->offset) {
+                       uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' would "
+                               "overflow control " UVC_GUID_FORMAT "/%u\n",
+                               mapping->name, UVC_GUID_ARGS(info->entity),
+                               info->selector);
+                       ret = -EOVERFLOW;
+                       goto end;
+               }
+
+               /* Check if the list contains a mapping matching the new one.
+                * Bail out if it does.
+                */
+               list_for_each_entry(map, &info->mappings, list) {
+                       if (map->id == mapping->id) {
+                               uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' is "
+                                       "already defined.\n", mapping->name);
+                               ret = -EEXIST;
+                               goto end;
+                       }
+               }
+
+               mapping->ctrl = info;
+               list_add_tail(&mapping->list, &info->mappings);
+               uvc_trace(UVC_TRACE_CONTROL, "Adding mapping %s to control "
+                       UVC_GUID_FORMAT "/%u.\n", mapping->name,
+                       UVC_GUID_ARGS(info->entity), info->selector);
+
+               ret = 0;
+               break;
+       }
+end:
+       mutex_unlock(&uvc_driver.ctrl_mutex);
+       return ret;
+}
+
+/*
+ * Initialize device controls.
+ */
+int uvc_ctrl_init_device(struct uvc_device *dev)
+{
+       struct uvc_control_info *info;
+       struct uvc_control *ctrl;
+       struct uvc_entity *entity;
+       unsigned int i;
+
+       /* Walk the entities list and instantiate controls */
+       list_for_each_entry(entity, &dev->entities, list) {
+               unsigned int bControlSize = 0, ncontrols = 0;
+               __u8 *bmControls = NULL;
+
+               if (UVC_ENTITY_TYPE(entity) == VC_EXTENSION_UNIT) {
+                       bmControls = entity->extension.bmControls;
+                       bControlSize = entity->extension.bControlSize;
+               } else if (UVC_ENTITY_TYPE(entity) == VC_PROCESSING_UNIT) {
+                       bmControls = entity->processing.bmControls;
+                       bControlSize = entity->processing.bControlSize;
+               } else if (UVC_ENTITY_TYPE(entity) == ITT_CAMERA) {
+                       bmControls = entity->camera.bmControls;
+                       bControlSize = entity->camera.bControlSize;
+               }
+
+               for (i = 0; i < bControlSize; ++i)
+                       ncontrols += hweight8(bmControls[i]);
+
+               if (ncontrols == 0)
+                       continue;
+
+               entity->controls = kzalloc(ncontrols*sizeof *ctrl, GFP_KERNEL);
+               if (entity->controls == NULL)
+                       return -ENOMEM;
+
+               entity->ncontrols = ncontrols;
+
+               ctrl = entity->controls;
+               for (i = 0; i < bControlSize * 8; ++i) {
+                       if (uvc_get_bit(bmControls, i) == 0)
+                               continue;
+
+                       ctrl->entity = entity;
+                       ctrl->index = i;
+                       ctrl++;
+               }
+       }
+
+       /* Walk the controls info list and associate them with the device
+        * controls, then add the device to the global device list. This has
+        * to be done while holding the controls lock, to make sure
+        * uvc_ctrl_add_info() will not get called in-between.
+        */
+       mutex_lock(&uvc_driver.ctrl_mutex);
+       list_for_each_entry(info, &uvc_driver.controls, list)
+               uvc_ctrl_add_ctrl(dev, info);
+
+       list_add_tail(&dev->list, &uvc_driver.devices);
+       mutex_unlock(&uvc_driver.ctrl_mutex);
+
+       return 0;
+}
+
+/*
+ * Cleanup device controls.
+ */
+void uvc_ctrl_cleanup_device(struct uvc_device *dev)
+{
+       struct uvc_entity *entity;
+       unsigned int i;
+
+       /* Remove the device from the global devices list */
+       mutex_lock(&uvc_driver.ctrl_mutex);
+       if (dev->list.next != NULL)
+               list_del(&dev->list);
+       mutex_unlock(&uvc_driver.ctrl_mutex);
+
+       list_for_each_entry(entity, &dev->entities, list) {
+               for (i = 0; i < entity->ncontrols; ++i)
+                       kfree(entity->controls[i].data);
+
+               kfree(entity->controls);
+       }
+}
+
+void uvc_ctrl_init(void)
+{
+       struct uvc_control_info *ctrl = uvc_ctrls;
+       struct uvc_control_info *cend = ctrl + ARRAY_SIZE(uvc_ctrls);
+       struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
+       struct uvc_control_mapping *mend =
+               mapping + ARRAY_SIZE(uvc_ctrl_mappings);
+
+       for (; ctrl < cend; ++ctrl)
+               uvc_ctrl_add_info(ctrl);
+
+       for (; mapping < mend; ++mapping)
+               uvc_ctrl_add_mapping(mapping);
+}
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
new file mode 100644 (file)
index 0000000..60ced58
--- /dev/null
@@ -0,0 +1,1955 @@
+/*
+ *      uvc_driver.c  --  USB Video Class driver
+ *
+ *      Copyright (C) 2005-2008
+ *          Laurent Pinchart (laurent.pinchart@skynet.be)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ */
+
+/*
+ * This driver aims to support video input 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
+ * software. If your user-space application doesn't support YUYV or MJPEG, fix
+ * it :-). Please note that the MJPEG data have been stripped from their
+ * Huffman tables (DHT marker), you will need to add it back if your JPEG
+ * codec can't handle MJPEG data.
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+
+#include <media/v4l2-common.h>
+
+#include "uvcvideo.h"
+
+#define DRIVER_AUTHOR          "Laurent Pinchart <laurent.pinchart@skynet.be>"
+#define DRIVER_DESC            "USB Video Class driver"
+#ifndef DRIVER_VERSION
+#define DRIVER_VERSION         "v0.1.0"
+#endif
+
+static unsigned int uvc_quirks_param;
+unsigned int uvc_trace_param;
+
+/* ------------------------------------------------------------------------
+ * Control, formats, ...
+ */
+
+static struct uvc_format_desc uvc_fmts[] = {
+       {
+               .name           = "YUV 4:2:2 (YUYV)",
+               .guid           = UVC_GUID_FORMAT_YUY2,
+               .fcc            = V4L2_PIX_FMT_YUYV,
+       },
+       {
+               .name           = "YUV 4:2:0 (NV12)",
+               .guid           = UVC_GUID_FORMAT_NV12,
+               .fcc            = V4L2_PIX_FMT_NV12,
+       },
+       {
+               .name           = "MJPEG",
+               .guid           = UVC_GUID_FORMAT_MJPEG,
+               .fcc            = V4L2_PIX_FMT_MJPEG,
+       },
+       {
+               .name           = "YVU 4:2:0 (YV12)",
+               .guid           = UVC_GUID_FORMAT_YV12,
+               .fcc            = V4L2_PIX_FMT_YVU420,
+       },
+       {
+               .name           = "YUV 4:2:0 (I420)",
+               .guid           = UVC_GUID_FORMAT_I420,
+               .fcc            = V4L2_PIX_FMT_YUV420,
+       },
+       {
+               .name           = "YUV 4:2:2 (UYVY)",
+               .guid           = UVC_GUID_FORMAT_UYVY,
+               .fcc            = V4L2_PIX_FMT_UYVY,
+       },
+       {
+               .name           = "Greyscale",
+               .guid           = UVC_GUID_FORMAT_Y800,
+               .fcc            = V4L2_PIX_FMT_GREY,
+       },
+       {
+               .name           = "RGB Bayer",
+               .guid           = UVC_GUID_FORMAT_BY8,
+               .fcc            = V4L2_PIX_FMT_SBGGR8,
+       },
+};
+
+/* ------------------------------------------------------------------------
+ * Utility functions
+ */
+
+struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
+               __u8 epaddr)
+{
+       struct usb_host_endpoint *ep;
+       unsigned int i;
+
+       for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
+               ep = &alts->endpoint[i];
+               if (ep->desc.bEndpointAddress == epaddr)
+                       return ep;
+       }
+
+       return NULL;
+}
+
+static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16])
+{
+       unsigned int len = ARRAY_SIZE(uvc_fmts);
+       unsigned int i;
+
+       for (i = 0; i < len; ++i) {
+               if (memcmp(guid, uvc_fmts[i].guid, 16) == 0)
+                       return &uvc_fmts[i];
+       }
+
+       return NULL;
+}
+
+static __u32 uvc_colorspace(const __u8 primaries)
+{
+       static const __u8 colorprimaries[] = {
+               0,
+               V4L2_COLORSPACE_SRGB,
+               V4L2_COLORSPACE_470_SYSTEM_M,
+               V4L2_COLORSPACE_470_SYSTEM_BG,
+               V4L2_COLORSPACE_SMPTE170M,
+               V4L2_COLORSPACE_SMPTE240M,
+       };
+
+       if (primaries < ARRAY_SIZE(colorprimaries))
+               return colorprimaries[primaries];
+
+       return 0;
+}
+
+/* Simplify a fraction using a simple continued fraction decomposition. The
+ * idea here is to convert fractions such as 333333/10000000 to 1/30 using
+ * 32 bit arithmetic only. The algorithm is not perfect and relies upon two
+ * arbitrary parameters to remove non-significative terms from the simple
+ * continued fraction decomposition. Using 8 and 333 for n_terms and threshold
+ * respectively seems to give nice results.
+ */
+void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,
+               unsigned int n_terms, unsigned int threshold)
+{
+       uint32_t *an;
+       uint32_t x, y, r;
+       unsigned int i, n;
+
+       an = kmalloc(n_terms * sizeof *an, GFP_KERNEL);
+       if (an == NULL)
+               return;
+
+       /* Convert the fraction to a simple continued fraction. See
+        * http://mathforum.org/dr.math/faq/faq.fractions.html
+        * Stop if the current term is bigger than or equal to the given
+        * threshold.
+        */
+       x = *numerator;
+       y = *denominator;
+
+       for (n = 0; n < n_terms && y != 0; ++n) {
+               an[n] = x / y;
+               if (an[n] >= threshold) {
+                       if (n < 2)
+                               n++;
+                       break;
+               }
+
+               r = x - an[n] * y;
+               x = y;
+               y = r;
+       }
+
+       /* Expand the simple continued fraction back to an integer fraction. */
+       x = 0;
+       y = 1;
+
+       for (i = n; i > 0; --i) {
+               r = y;
+               y = an[i-1] * y + x;
+               x = r;
+       }
+
+       *numerator = y;
+       *denominator = x;
+       kfree(an);
+}
+
+/* Convert a fraction to a frame interval in 100ns multiples. The idea here is
+ * to compute numerator / denominator * 10000000 using 32 bit fixed point
+ * arithmetic only.
+ */
+uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator)
+{
+       uint32_t multiplier;
+
+       /* Saturate the result if the operation would overflow. */
+       if (denominator == 0 ||
+           numerator/denominator >= ((uint32_t)-1)/10000000)
+               return (uint32_t)-1;
+
+       /* Divide both the denominator and the multiplier by two until
+        * numerator * multiplier doesn't overflow. If anyone knows a better
+        * algorithm please let me know.
+        */
+       multiplier = 10000000;
+       while (numerator > ((uint32_t)-1)/multiplier) {
+               multiplier /= 2;
+               denominator /= 2;
+       }
+
+       return denominator ? numerator * multiplier / denominator : 0;
+}
+
+/* ------------------------------------------------------------------------
+ * Terminal and unit management
+ */
+
+static struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id)
+{
+       struct uvc_entity *entity;
+
+       list_for_each_entry(entity, &dev->entities, list) {
+               if (entity->id == id)
+                       return entity;
+       }
+
+       return NULL;
+}
+
+static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
+       int id, struct uvc_entity *entity)
+{
+       unsigned int i;
+
+       if (entity == NULL)
+               entity = list_entry(&dev->entities, struct uvc_entity, list);
+
+       list_for_each_entry_continue(entity, &dev->entities, list) {
+               switch (UVC_ENTITY_TYPE(entity)) {
+               case TT_STREAMING:
+                       if (entity->output.bSourceID == id)
+                               return entity;
+                       break;
+
+               case VC_PROCESSING_UNIT:
+                       if (entity->processing.bSourceID == id)
+                               return entity;
+                       break;
+
+               case VC_SELECTOR_UNIT:
+                       for (i = 0; i < entity->selector.bNrInPins; ++i)
+                               if (entity->selector.baSourceID[i] == id)
+                                       return entity;
+                       break;
+
+               case VC_EXTENSION_UNIT:
+                       for (i = 0; i < entity->extension.bNrInPins; ++i)
+                               if (entity->extension.baSourceID[i] == id)
+                                       return entity;
+                       break;
+               }
+       }
+
+       return NULL;
+}
+
+/* ------------------------------------------------------------------------
+ * Descriptors handling
+ */
+
+static int uvc_parse_format(struct uvc_device *dev,
+       struct uvc_streaming *streaming, struct uvc_format *format,
+       __u32 **intervals, unsigned char *buffer, int buflen)
+{
+       struct usb_interface *intf = streaming->intf;
+       struct usb_host_interface *alts = intf->cur_altsetting;
+       struct uvc_format_desc *fmtdesc;
+       struct uvc_frame *frame;
+       const unsigned char *start = buffer;
+       unsigned int interval;
+       unsigned int i, n;
+       __u8 ftype;
+
+       format->type = buffer[2];
+       format->index = buffer[3];
+
+       switch (buffer[2]) {
+       case VS_FORMAT_UNCOMPRESSED:
+       case VS_FORMAT_FRAME_BASED:
+               if (buflen < 27) {
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+                              "interface %d FORMAT error\n",
+                              dev->udev->devnum,
+                              alts->desc.bInterfaceNumber);
+                       return -EINVAL;
+               }
+
+               /* Find the format descriptor from its GUID. */
+               fmtdesc = uvc_format_by_guid(&buffer[5]);
+
+               if (fmtdesc != NULL) {
+                       strncpy(format->name, fmtdesc->name,
+                               sizeof format->name);
+                       format->fcc = fmtdesc->fcc;
+               } else {
+                       uvc_printk(KERN_INFO, "Unknown video format "
+                               UVC_GUID_FORMAT "\n",
+                               UVC_GUID_ARGS(&buffer[5]));
+                       snprintf(format->name, sizeof format->name,
+                               UVC_GUID_FORMAT, UVC_GUID_ARGS(&buffer[5]));
+                       format->fcc = 0;
+               }
+
+               format->bpp = buffer[21];
+               if (buffer[2] == VS_FORMAT_UNCOMPRESSED) {
+                       ftype = VS_FRAME_UNCOMPRESSED;
+               } else {
+                       ftype = VS_FRAME_FRAME_BASED;
+                       if (buffer[27])
+                               format->flags = UVC_FMT_FLAG_COMPRESSED;
+               }
+               break;
+
+       case VS_FORMAT_MJPEG:
+               if (buflen < 11) {
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+                              "interface %d FORMAT error\n",
+                              dev->udev->devnum,
+                              alts->desc.bInterfaceNumber);
+                       return -EINVAL;
+               }
+
+               strncpy(format->name, "MJPEG", sizeof format->name);
+               format->fcc = V4L2_PIX_FMT_MJPEG;
+               format->flags = UVC_FMT_FLAG_COMPRESSED;
+               format->bpp = 0;
+               ftype = VS_FRAME_MJPEG;
+               break;
+
+       case VS_FORMAT_DV:
+               if (buflen < 9) {
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+                              "interface %d FORMAT error\n",
+                              dev->udev->devnum,
+                              alts->desc.bInterfaceNumber);
+                       return -EINVAL;
+               }
+
+               switch (buffer[8] & 0x7f) {
+               case 0:
+                       strncpy(format->name, "SD-DV", sizeof format->name);
+                       break;
+               case 1:
+                       strncpy(format->name, "SDL-DV", sizeof format->name);
+                       break;
+               case 2:
+                       strncpy(format->name, "HD-DV", sizeof format->name);
+                       break;
+               default:
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+                              "interface %d: unknown DV format %u\n",
+                              dev->udev->devnum,
+                              alts->desc.bInterfaceNumber, buffer[8]);
+                       return -EINVAL;
+               }
+
+               strncat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz",
+                       sizeof format->name);
+
+               format->fcc = V4L2_PIX_FMT_DV;
+               format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM;
+               format->bpp = 0;
+               ftype = 0;
+
+               /* Create a dummy frame descriptor. */
+               frame = &format->frame[0];
+               memset(&format->frame[0], 0, sizeof format->frame[0]);
+               frame->bFrameIntervalType = 1;
+               frame->dwDefaultFrameInterval = 1;
+               frame->dwFrameInterval = *intervals;
+               *(*intervals)++ = 1;
+               format->nframes = 1;
+               break;
+
+       case VS_FORMAT_MPEG2TS:
+       case VS_FORMAT_STREAM_BASED:
+               /* Not supported yet. */
+       default:
+               uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+                      "interface %d unsupported format %u\n",
+                      dev->udev->devnum, alts->desc.bInterfaceNumber,
+                      buffer[2]);
+               return -EINVAL;
+       }
+
+       uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name);
+
+       buflen -= buffer[0];
+       buffer += buffer[0];
+
+       /* 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
+                       n = buflen > 21 ? buffer[21] : 0;
+
+               n = n ? n : 3;
+
+               if (buflen < 26 + 4*n) {
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+                              "interface %d FRAME error\n", dev->udev->devnum,
+                              alts->desc.bInterfaceNumber);
+                       return -EINVAL;
+               }
+
+               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]);
+               if (ftype != VS_FRAME_FRAME_BASED) {
+                       frame->dwMaxVideoFrameBufferSize =
+                               le32_to_cpup((__le32 *)&buffer[17]);
+                       frame->dwDefaultFrameInterval =
+                               le32_to_cpup((__le32 *)&buffer[21]);
+                       frame->bFrameIntervalType = buffer[25];
+               } else {
+                       frame->dwMaxVideoFrameBufferSize = 0;
+                       frame->dwDefaultFrameInterval =
+                               le32_to_cpup((__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
+                * 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
+                * value from the frame size.
+                */
+               if (!(format->flags & UVC_FMT_FLAG_COMPRESSED))
+                       frame->dwMaxVideoFrameBufferSize = format->bpp
+                               * frame->wWidth * frame->wHeight / 8;
+
+               /* 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.
+                */
+               for (i = 0; i < n; ++i) {
+                       interval = le32_to_cpup((__le32 *)&buffer[26+4*i]);
+                       *(*intervals)++ = interval ? interval : 1;
+               }
+
+               /* Make sure that the default frame interval stays between
+                * the boundaries.
+                */
+               n -= frame->bFrameIntervalType ? 1 : 2;
+               frame->dwDefaultFrameInterval =
+                       min(frame->dwFrameInterval[n],
+                           max(frame->dwFrameInterval[0],
+                               frame->dwDefaultFrameInterval));
+
+               uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n",
+                       frame->wWidth, frame->wHeight,
+                       10000000/frame->dwDefaultFrameInterval,
+                       (100000000/frame->dwDefaultFrameInterval)%10);
+
+               format->nframes++;
+               buflen -= buffer[0];
+               buffer += buffer[0];
+       }
+
+       if (buflen > 2 && buffer[2] == VS_STILL_IMAGE_FRAME) {
+               buflen -= buffer[0];
+               buffer += buffer[0];
+       }
+
+       if (buflen > 2 && buffer[2] == VS_COLORFORMAT) {
+               if (buflen < 6) {
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+                              "interface %d COLORFORMAT error\n",
+                              dev->udev->devnum,
+                              alts->desc.bInterfaceNumber);
+                       return -EINVAL;
+               }
+
+               format->colorspace = uvc_colorspace(buffer[3]);
+
+               buflen -= buffer[0];
+               buffer += buffer[0];
+       }
+
+       return buffer - start;
+}
+
+static int uvc_parse_streaming(struct uvc_device *dev,
+       struct usb_interface *intf)
+{
+       struct uvc_streaming *streaming = NULL;
+       struct uvc_format *format;
+       struct uvc_frame *frame;
+       struct usb_host_interface *alts = &intf->altsetting[0];
+       unsigned char *_buffer, *buffer = alts->extra;
+       int _buflen, buflen = alts->extralen;
+       unsigned int nformats = 0, nframes = 0, nintervals = 0;
+       unsigned int size, i, n, p;
+       __u32 *interval;
+       __u16 psize;
+       int ret = -EINVAL;
+
+       if (intf->cur_altsetting->desc.bInterfaceSubClass
+               != SC_VIDEOSTREAMING) {
+               uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a "
+                       "video streaming interface\n", dev->udev->devnum,
+                       intf->altsetting[0].desc.bInterfaceNumber);
+               return -EINVAL;
+       }
+
+       if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) {
+               uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already "
+                       "claimed\n", dev->udev->devnum,
+                       intf->altsetting[0].desc.bInterfaceNumber);
+               return -EINVAL;
+       }
+
+       streaming = kzalloc(sizeof *streaming, GFP_KERNEL);
+       if (streaming == NULL) {
+               usb_driver_release_interface(&uvc_driver.driver, intf);
+               return -EINVAL;
+       }
+
+       mutex_init(&streaming->mutex);
+       streaming->intf = usb_get_intf(intf);
+       streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
+
+       /* The Pico iMage webcam has its class-specific interface descriptors
+        * after the endpoint descriptors.
+        */
+       if (buflen == 0) {
+               for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
+                       struct usb_host_endpoint *ep = &alts->endpoint[i];
+
+                       if (ep->extralen == 0)
+                               continue;
+
+                       if (ep->extralen > 2 &&
+                           ep->extra[1] == USB_DT_CS_INTERFACE) {
+                               uvc_trace(UVC_TRACE_DESCR, "trying extra data "
+                                       "from endpoint %u.\n", i);
+                               buffer = alts->endpoint[i].extra;
+                               buflen = alts->endpoint[i].extralen;
+                               break;
+                       }
+               }
+       }
+
+       /* Skip the standard interface descriptors. */
+       while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) {
+               buflen -= buffer[0];
+               buffer += buffer[0];
+       }
+
+       if (buflen <= 2) {
+               uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming "
+                       "interface descriptors found.\n");
+               goto error;
+       }
+
+       /* Parse the header descriptor. */
+       if (buffer[2] == VS_OUTPUT_HEADER) {
+               uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
+                       "%d OUTPUT HEADER descriptor is not supported.\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;
+               }
+
+               streaming->header.bNumFormats = p;
+               streaming->header.bEndpointAddress = buffer[6];
+               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);
+               goto error;
+       }
+
+       buflen -= buffer[0];
+       buffer += buffer[0];
+
+       _buffer = buffer;
+       _buflen = buflen;
+
+       /* Count the format and frame descriptors. */
+       while (_buflen > 2) {
+               switch (_buffer[2]) {
+               case VS_FORMAT_UNCOMPRESSED:
+               case VS_FORMAT_MJPEG:
+               case VS_FORMAT_FRAME_BASED:
+                       nformats++;
+                       break;
+
+               case VS_FORMAT_DV:
+                       /* DV format has no frame descriptor. We will create a
+                        * dummy frame descriptor with a dummy frame interval.
+                        */
+                       nformats++;
+                       nframes++;
+                       nintervals++;
+                       break;
+
+               case VS_FORMAT_MPEG2TS:
+               case VS_FORMAT_STREAM_BASED:
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+                               "interface %d FORMAT %u is not supported.\n",
+                               dev->udev->devnum,
+                               alts->desc.bInterfaceNumber, _buffer[2]);
+                       break;
+
+               case VS_FRAME_UNCOMPRESSED:
+               case VS_FRAME_MJPEG:
+                       nframes++;
+                       if (_buflen > 25)
+                               nintervals += _buffer[25] ? _buffer[25] : 3;
+                       break;
+
+               case VS_FRAME_FRAME_BASED:
+                       nframes++;
+                       if (_buflen > 21)
+                               nintervals += _buffer[21] ? _buffer[21] : 3;
+                       break;
+               }
+
+               _buflen -= _buffer[0];
+               _buffer += _buffer[0];
+       }
+
+       if (nformats == 0) {
+               uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
+                       "%d has no supported formats defined.\n",
+                       dev->udev->devnum, alts->desc.bInterfaceNumber);
+               goto error;
+       }
+
+       size = nformats * sizeof *format + nframes * sizeof *frame
+            + nintervals * sizeof *interval;
+       format = kzalloc(size, GFP_KERNEL);
+       if (format == NULL) {
+               ret = -ENOMEM;
+               goto error;
+       }
+
+       frame = (struct uvc_frame *)&format[nformats];
+       interval = (__u32 *)&frame[nframes];
+
+       streaming->format = format;
+       streaming->nformats = nformats;
+
+       /* Parse the format descriptors. */
+       while (buflen > 2) {
+               switch (buffer[2]) {
+               case VS_FORMAT_UNCOMPRESSED:
+               case VS_FORMAT_MJPEG:
+               case VS_FORMAT_DV:
+               case VS_FORMAT_FRAME_BASED:
+                       format->frame = frame;
+                       ret = uvc_parse_format(dev, streaming, format,
+                               &interval, buffer, buflen);
+                       if (ret < 0)
+                               goto error;
+
+                       frame += format->nframes;
+                       format++;
+
+                       buflen -= ret;
+                       buffer += ret;
+                       continue;
+
+               default:
+                       break;
+               }
+
+               buflen -= buffer[0];
+               buffer += buffer[0];
+       }
+
+       /* Parse the alternate settings to find the maximum bandwidth. */
+       for (i = 0; i < intf->num_altsetting; ++i) {
+               struct usb_host_endpoint *ep;
+               alts = &intf->altsetting[i];
+               ep = uvc_find_endpoint(alts,
+                               streaming->header.bEndpointAddress);
+               if (ep == NULL)
+                       continue;
+
+               psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+               psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+               if (psize > streaming->maxpsize)
+                       streaming->maxpsize = psize;
+       }
+
+       list_add_tail(&streaming->list, &dev->streaming);
+       return 0;
+
+error:
+       usb_driver_release_interface(&uvc_driver.driver, intf);
+       usb_put_intf(intf);
+       kfree(streaming->format);
+       kfree(streaming->header.bmaControls);
+       kfree(streaming);
+       return ret;
+}
+
+/* Parse vendor-specific extensions. */
+static int uvc_parse_vendor_control(struct uvc_device *dev,
+       const unsigned char *buffer, int buflen)
+{
+       struct usb_device *udev = dev->udev;
+       struct usb_host_interface *alts = dev->intf->cur_altsetting;
+       struct uvc_entity *unit;
+       unsigned int n, p;
+       int handled = 0;
+
+       switch (le16_to_cpu(dev->udev->descriptor.idVendor)) {
+       case 0x046d:            /* Logitech */
+               if (buffer[1] != 0x41 || buffer[2] != 0x01)
+                       break;
+
+               /* Logitech implements several vendor specific functions
+                * through vendor specific extension units (LXU).
+                *
+                * The LXU descriptors are similar to XU descriptors
+                * (see "USB Device Video Class for Video Devices", section
+                * 3.7.2.6 "Extension Unit Descriptor") with the following
+                * differences:
+                *
+                * ----------------------------------------------------------
+                * 0            bLength         1        Number
+                *      Size of this descriptor, in bytes: 24+p+n*2
+                * ----------------------------------------------------------
+                * 23+p+n       bmControlsType  N       Bitmap
+                *      Individual bits in the set are defined:
+                *      0: Absolute
+                *      1: Relative
+                *
+                *      This bitset is mapped exactly the same as bmControls.
+                * ----------------------------------------------------------
+                * 23+p+n*2     bReserved       1       Boolean
+                * ----------------------------------------------------------
+                * 24+p+n*2     iExtension      1       Index
+                *      Index of a string descriptor that describes this
+                *      extension unit.
+                * ----------------------------------------------------------
+                */
+               p = buflen >= 22 ? buffer[21] : 0;
+               n = buflen >= 25 + p ? buffer[22+p] : 0;
+
+               if (buflen < 25 + p + 2*n) {
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+                               "interface %d EXTENSION_UNIT error\n",
+                               udev->devnum, alts->desc.bInterfaceNumber);
+                       break;
+               }
+
+               unit = kzalloc(sizeof *unit + p + 2*n, GFP_KERNEL);
+               if (unit == NULL)
+                       return -ENOMEM;
+
+               unit->id = buffer[3];
+               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.baSourceID = (__u8 *)unit + sizeof *unit;
+               memcpy(unit->extension.baSourceID, &buffer[22], p);
+               unit->extension.bControlSize = buffer[22+p];
+               unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p;
+               unit->extension.bmControlsType = (__u8 *)unit + sizeof *unit
+                                              + p + n;
+               memcpy(unit->extension.bmControls, &buffer[23+p], 2*n);
+
+               if (buffer[24+p+2*n] != 0)
+                       usb_string(udev, buffer[24+p+2*n], unit->name,
+                                  sizeof unit->name);
+               else
+                       sprintf(unit->name, "Extension %u", buffer[3]);
+
+               list_add_tail(&unit->list, &dev->entities);
+               handled = 1;
+               break;
+       }
+
+       return handled;
+}
+
+static int uvc_parse_standard_control(struct uvc_device *dev,
+       const unsigned char *buffer, int buflen)
+{
+       struct usb_device *udev = dev->udev;
+       struct uvc_entity *unit, *term;
+       struct usb_interface *intf;
+       struct usb_host_interface *alts = dev->intf->cur_altsetting;
+       unsigned int i, n, p, len;
+       __u16 type;
+
+       switch (buffer[2]) {
+       case VC_HEADER:
+               n = buflen >= 12 ? buffer[11] : 0;
+
+               if (buflen < 12 || buflen < 12 + n) {
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+                               "interface %d HEADER error\n", udev->devnum,
+                               alts->desc.bInterfaceNumber);
+                       return -EINVAL;
+               }
+
+               dev->uvc_version = le16_to_cpup((__le16 *)&buffer[3]);
+               dev->clock_frequency = le32_to_cpup((__le32 *)&buffer[7]);
+
+               /* Parse all USB Video Streaming interfaces. */
+               for (i = 0; i < n; ++i) {
+                       intf = usb_ifnum_to_if(udev, buffer[12+i]);
+                       if (intf == NULL) {
+                               uvc_trace(UVC_TRACE_DESCR, "device %d "
+                                       "interface %d doesn't exists\n",
+                                       udev->devnum, i);
+                               continue;
+                       }
+
+                       uvc_parse_streaming(dev, intf);
+               }
+               break;
+
+       case VC_INPUT_TERMINAL:
+               if (buflen < 8) {
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+                               "interface %d INPUT_TERMINAL error\n",
+                               udev->devnum, alts->desc.bInterfaceNumber);
+                       return -EINVAL;
+               }
+
+               /* Make sure the terminal type MSB is not null, otherwise it
+                * could be confused with a unit.
+                */
+               type = le16_to_cpup((__le16 *)&buffer[4]);
+               if ((type & 0xff00) == 0) {
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+                               "interface %d INPUT_TERMINAL %d has invalid "
+                               "type 0x%04x, skipping\n", udev->devnum,
+                               alts->desc.bInterfaceNumber,
+                               buffer[3], type);
+                       return 0;
+               }
+
+               n = 0;
+               p = 0;
+               len = 8;
+
+               if (type == ITT_CAMERA) {
+                       n = buflen >= 15 ? buffer[14] : 0;
+                       len = 15;
+
+               } else if (type == ITT_MEDIA_TRANSPORT_INPUT) {
+                       n = buflen >= 9 ? buffer[8] : 0;
+                       p = buflen >= 10 + n ? buffer[9+n] : 0;
+                       len = 10;
+               }
+
+               if (buflen < len + n + p) {
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+                               "interface %d INPUT_TERMINAL error\n",
+                               udev->devnum, alts->desc.bInterfaceNumber);
+                       return -EINVAL;
+               }
+
+               term = kzalloc(sizeof *term + n + p, GFP_KERNEL);
+               if (term == NULL)
+                       return -ENOMEM;
+
+               term->id = buffer[3];
+               term->type = type | UVC_TERM_INPUT;
+
+               if (UVC_ENTITY_TYPE(term) == ITT_CAMERA) {
+                       term->camera.bControlSize = n;
+                       term->camera.bmControls = (__u8 *)term + sizeof *term;
+                       term->camera.wObjectiveFocalLengthMin =
+                               le16_to_cpup((__le16 *)&buffer[8]);
+                       term->camera.wObjectiveFocalLengthMax =
+                               le16_to_cpup((__le16 *)&buffer[10]);
+                       term->camera.wOcularFocalLength =
+                               le16_to_cpup((__le16 *)&buffer[12]);
+                       memcpy(term->camera.bmControls, &buffer[15], n);
+               } else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) {
+                       term->media.bControlSize = n;
+                       term->media.bmControls = (__u8 *)term + sizeof *term;
+                       term->media.bTransportModeSize = p;
+                       term->media.bmTransportModes = (__u8 *)term
+                                                    + sizeof *term + n;
+                       memcpy(term->media.bmControls, &buffer[9], n);
+                       memcpy(term->media.bmTransportModes, &buffer[10+n], p);
+               }
+
+               if (buffer[7] != 0)
+                       usb_string(udev, buffer[7], term->name,
+                                  sizeof term->name);
+               else if (UVC_ENTITY_TYPE(term) == ITT_CAMERA)
+                       sprintf(term->name, "Camera %u", buffer[3]);
+               else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT)
+                       sprintf(term->name, "Media %u", buffer[3]);
+               else
+                       sprintf(term->name, "Input %u", buffer[3]);
+
+               list_add_tail(&term->list, &dev->entities);
+               break;
+
+       case VC_OUTPUT_TERMINAL:
+               if (buflen < 9) {
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+                               "interface %d OUTPUT_TERMINAL error\n",
+                               udev->devnum, alts->desc.bInterfaceNumber);
+                       return -EINVAL;
+               }
+
+               /* Make sure the terminal type MSB is not null, otherwise it
+                * could be confused with a unit.
+                */
+               type = le16_to_cpup((__le16 *)&buffer[4]);
+               if ((type & 0xff00) == 0) {
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+                               "interface %d OUTPUT_TERMINAL %d has invalid "
+                               "type 0x%04x, skipping\n", udev->devnum,
+                               alts->desc.bInterfaceNumber, buffer[3], type);
+                       return 0;
+               }
+
+               term = kzalloc(sizeof *term, GFP_KERNEL);
+               if (term == NULL)
+                       return -ENOMEM;
+
+               term->id = buffer[3];
+               term->type = type | UVC_TERM_OUTPUT;
+               term->output.bSourceID = buffer[7];
+
+               if (buffer[8] != 0)
+                       usb_string(udev, buffer[8], term->name,
+                                  sizeof term->name);
+               else
+                       sprintf(term->name, "Output %u", buffer[3]);
+
+               list_add_tail(&term->list, &dev->entities);
+               break;
+
+       case VC_SELECTOR_UNIT:
+               p = buflen >= 5 ? buffer[4] : 0;
+
+               if (buflen < 5 || buflen < 6 + p) {
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+                               "interface %d SELECTOR_UNIT error\n",
+                               udev->devnum, alts->desc.bInterfaceNumber);
+                       return -EINVAL;
+               }
+
+               unit = kzalloc(sizeof *unit + p, GFP_KERNEL);
+               if (unit == NULL)
+                       return -ENOMEM;
+
+               unit->id = buffer[3];
+               unit->type = buffer[2];
+               unit->selector.bNrInPins = buffer[4];
+               unit->selector.baSourceID = (__u8 *)unit + sizeof *unit;
+               memcpy(unit->selector.baSourceID, &buffer[5], p);
+
+               if (buffer[5+p] != 0)
+                       usb_string(udev, buffer[5+p], unit->name,
+                                  sizeof unit->name);
+               else
+                       sprintf(unit->name, "Selector %u", buffer[3]);
+
+               list_add_tail(&unit->list, &dev->entities);
+               break;
+
+       case VC_PROCESSING_UNIT:
+               n = buflen >= 8 ? buffer[7] : 0;
+               p = dev->uvc_version >= 0x0110 ? 10 : 9;
+
+               if (buflen < p + n) {
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+                               "interface %d PROCESSING_UNIT error\n",
+                               udev->devnum, alts->desc.bInterfaceNumber);
+                       return -EINVAL;
+               }
+
+               unit = kzalloc(sizeof *unit + n, GFP_KERNEL);
+               if (unit == NULL)
+                       return -ENOMEM;
+
+               unit->id = buffer[3];
+               unit->type = buffer[2];
+               unit->processing.bSourceID = buffer[4];
+               unit->processing.wMaxMultiplier =
+                       le16_to_cpup((__le16 *)&buffer[5]);
+               unit->processing.bControlSize = buffer[7];
+               unit->processing.bmControls = (__u8 *)unit + sizeof *unit;
+               memcpy(unit->processing.bmControls, &buffer[8], n);
+               if (dev->uvc_version >= 0x0110)
+                       unit->processing.bmVideoStandards = buffer[9+n];
+
+               if (buffer[8+n] != 0)
+                       usb_string(udev, buffer[8+n], unit->name,
+                                  sizeof unit->name);
+               else
+                       sprintf(unit->name, "Processing %u", buffer[3]);
+
+               list_add_tail(&unit->list, &dev->entities);
+               break;
+
+       case VC_EXTENSION_UNIT:
+               p = buflen >= 22 ? buffer[21] : 0;
+               n = buflen >= 24 + p ? buffer[22+p] : 0;
+
+               if (buflen < 24 + p + n) {
+                       uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
+                               "interface %d EXTENSION_UNIT error\n",
+                               udev->devnum, alts->desc.bInterfaceNumber);
+                       return -EINVAL;
+               }
+
+               unit = kzalloc(sizeof *unit + p + n, GFP_KERNEL);
+               if (unit == NULL)
+                       return -ENOMEM;
+
+               unit->id = buffer[3];
+               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.baSourceID = (__u8 *)unit + sizeof *unit;
+               memcpy(unit->extension.baSourceID, &buffer[22], p);
+               unit->extension.bControlSize = buffer[22+p];
+               unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p;
+               memcpy(unit->extension.bmControls, &buffer[23+p], n);
+
+               if (buffer[23+p+n] != 0)
+                       usb_string(udev, buffer[23+p+n], unit->name,
+                                  sizeof unit->name);
+               else
+                       sprintf(unit->name, "Extension %u", buffer[3]);
+
+               list_add_tail(&unit->list, &dev->entities);
+               break;
+
+       default:
+               uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE "
+                       "descriptor (%u)\n", buffer[2]);
+               break;
+       }
+
+       return 0;
+}
+
+static int uvc_parse_control(struct uvc_device *dev)
+{
+       struct usb_host_interface *alts = dev->intf->cur_altsetting;
+       unsigned char *buffer = alts->extra;
+       int buflen = alts->extralen;
+       int ret;
+
+       /* Parse the default alternate setting only, as the UVC specification
+        * defines a single alternate setting, the default alternate setting
+        * zero.
+        */
+
+       while (buflen > 2) {
+               if (uvc_parse_vendor_control(dev, buffer, buflen) ||
+                   buffer[1] != USB_DT_CS_INTERFACE)
+                       goto next_descriptor;
+
+               if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0)
+                       return ret;
+
+next_descriptor:
+               buflen -= buffer[0];
+               buffer += buffer[0];
+       }
+
+       /* Check if the optional status endpoint is present. */
+       if (alts->desc.bNumEndpoints == 1) {
+               struct usb_host_endpoint *ep = &alts->endpoint[0];
+               struct usb_endpoint_descriptor *desc = &ep->desc;
+
+               if (usb_endpoint_is_int_in(desc) &&
+                   le16_to_cpu(desc->wMaxPacketSize) >= 8 &&
+                   desc->bInterval != 0) {
+                       uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint "
+                               "(addr %02x).\n", desc->bEndpointAddress);
+                       dev->int_ep = ep;
+               }
+       }
+
+       return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * USB probe and disconnect
+ */
+
+/*
+ * Unregister the video devices.
+ */
+static void uvc_unregister_video(struct uvc_device *dev)
+{
+       if (dev->video.vdev) {
+               if (dev->video.vdev->minor == -1)
+                       video_device_release(dev->video.vdev);
+               else
+                       video_unregister_device(dev->video.vdev);
+               dev->video.vdev = NULL;
+       }
+}
+
+/*
+ * Scan the UVC descriptors to locate a chain starting at an Output Terminal
+ * and containing the following units:
+ *
+ * - a USB Streaming Output Terminal
+ * - 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.
+ *
+ * A side forward scan is made on each detected entity to check for additional
+ * extension units.
+ */
+static int uvc_scan_chain_entity(struct uvc_video_device *video,
+       struct uvc_entity *entity)
+{
+       switch (UVC_ENTITY_TYPE(entity)) {
+       case VC_EXTENSION_UNIT:
+               if (uvc_trace_param & UVC_TRACE_PROBE)
+                       printk(" <- XU %d", entity->id);
+
+               if (entity->extension.bNrInPins != 1) {
+                       uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more "
+                               "than 1 input pin.\n", entity->id);
+                       return -1;
+               }
+
+               list_add_tail(&entity->chain, &video->extensions);
+               break;
+
+       case VC_PROCESSING_UNIT:
+               if (uvc_trace_param & UVC_TRACE_PROBE)
+                       printk(" <- PU %d", entity->id);
+
+               if (video->processing != NULL) {
+                       uvc_trace(UVC_TRACE_DESCR, "Found multiple "
+                               "Processing Units in chain.\n");
+                       return -1;
+               }
+
+               video->processing = entity;
+               break;
+
+       case VC_SELECTOR_UNIT:
+               if (uvc_trace_param & UVC_TRACE_PROBE)
+                       printk(" <- SU %d", entity->id);
+
+               /* Single-input selector units are ignored. */
+               if (entity->selector.bNrInPins == 1)
+                       break;
+
+               if (video->selector != NULL) {
+                       uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector "
+                               "Units in chain.\n");
+                       return -1;
+               }
+
+               video->selector = entity;
+               break;
+
+       case ITT_VENDOR_SPECIFIC:
+       case ITT_CAMERA:
+       case ITT_MEDIA_TRANSPORT_INPUT:
+               if (uvc_trace_param & UVC_TRACE_PROBE)
+                       printk(" <- IT %d\n", entity->id);
+
+               list_add_tail(&entity->chain, &video->iterms);
+               break;
+
+       default:
+               uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type "
+                       "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
+               return -1;
+       }
+
+       return 0;
+}
+
+static int uvc_scan_chain_forward(struct uvc_video_device *video,
+       struct uvc_entity *entity, struct uvc_entity *prev)
+{
+       struct uvc_entity *forward;
+       int found;
+
+       /* Forward scan */
+       forward = NULL;
+       found = 0;
+
+       while (1) {
+               forward = uvc_entity_by_reference(video->dev, entity->id,
+                       forward);
+               if (forward == NULL)
+                       break;
+
+               if (UVC_ENTITY_TYPE(forward) != VC_EXTENSION_UNIT ||
+                   forward == prev)
+                       continue;
+
+               if (forward->extension.bNrInPins != 1) {
+                       uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has"
+                               "more than 1 input pin.\n", entity->id);
+                       return -1;
+               }
+
+               list_add_tail(&forward->chain, &video->extensions);
+               if (uvc_trace_param & UVC_TRACE_PROBE) {
+                       if (!found)
+                               printk(" (-> XU");
+
+                       printk(" %d", forward->id);
+                       found = 1;
+               }
+       }
+       if (found)
+               printk(")");
+
+       return 0;
+}
+
+static int uvc_scan_chain_backward(struct uvc_video_device *video,
+       struct uvc_entity *entity)
+{
+       struct uvc_entity *term;
+       int id = -1, i;
+
+       switch (UVC_ENTITY_TYPE(entity)) {
+       case VC_EXTENSION_UNIT:
+               id = entity->extension.baSourceID[0];
+               break;
+
+       case VC_PROCESSING_UNIT:
+               id = entity->processing.bSourceID;
+               break;
+
+       case VC_SELECTOR_UNIT:
+               /* Single-input selector units are ignored. */
+               if (entity->selector.bNrInPins == 1) {
+                       id = entity->selector.baSourceID[0];
+                       break;
+               }
+
+               if (uvc_trace_param & UVC_TRACE_PROBE)
+                       printk(" <- IT");
+
+               video->selector = entity;
+               for (i = 0; i < entity->selector.bNrInPins; ++i) {
+                       id = entity->selector.baSourceID[i];
+                       term = uvc_entity_by_id(video->dev, id);
+                       if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
+                               uvc_trace(UVC_TRACE_DESCR, "Selector unit %d "
+                                       "input %d isn't connected to an "
+                                       "input terminal\n", entity->id, i);
+                               return -1;
+                       }
+
+                       if (uvc_trace_param & UVC_TRACE_PROBE)
+                               printk(" %d", term->id);
+
+                       list_add_tail(&term->chain, &video->iterms);
+                       uvc_scan_chain_forward(video, term, entity);
+               }
+
+               if (uvc_trace_param & UVC_TRACE_PROBE)
+                       printk("\n");
+
+               id = 0;
+               break;
+       }
+
+       return id;
+}
+
+static int uvc_scan_chain(struct uvc_video_device *video)
+{
+       struct uvc_entity *entity, *prev;
+       int id;
+
+       entity = video->oterm;
+       uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id);
+       id = entity->output.bSourceID;
+       while (id != 0) {
+               prev = entity;
+               entity = uvc_entity_by_id(video->dev, id);
+               if (entity == NULL) {
+                       uvc_trace(UVC_TRACE_DESCR, "Found reference to "
+                               "unknown entity %d.\n", id);
+                       return -1;
+               }
+
+               /* Process entity */
+               if (uvc_scan_chain_entity(video, entity) < 0)
+                       return -1;
+
+               /* Forward scan */
+               if (uvc_scan_chain_forward(video, entity, prev) < 0)
+                       return -1;
+
+               /* Stop when a terminal is found. */
+               if (!UVC_ENTITY_IS_UNIT(entity))
+                       break;
+
+               /* Backward scan */
+               id = uvc_scan_chain_backward(video, entity);
+               if (id < 0)
+                       return id;
+       }
+
+       /* Initialize the video buffers queue. */
+       uvc_queue_init(&video->queue);
+
+       return 0;
+}
+
+/*
+ * Register the video devices.
+ *
+ * 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
+ *
+ * The Extension Units, if present, must have a single input pin. The
+ * Processing Unit and Extension Units can be in any order. Additional
+ * Extension Units connected to the main chain as single-unit branches are
+ * also supported.
+ */
+static int uvc_register_video(struct uvc_device *dev)
+{
+       struct video_device *vdev;
+       struct uvc_entity *term;
+       int found = 0, ret;
+
+       /* Check if the control interface matches the structure we expect. */
+       list_for_each_entry(term, &dev->entities, list) {
+               struct uvc_streaming *streaming;
+
+               if (UVC_ENTITY_TYPE(term) != TT_STREAMING)
+                       continue;
+
+               memset(&dev->video, 0, sizeof dev->video);
+               mutex_init(&dev->video.ctrl_mutex);
+               INIT_LIST_HEAD(&dev->video.iterms);
+               INIT_LIST_HEAD(&dev->video.extensions);
+               dev->video.oterm = term;
+               dev->video.dev = dev;
+               if (uvc_scan_chain(&dev->video) < 0)
+                       continue;
+
+               list_for_each_entry(streaming, &dev->streaming, list) {
+                       if (streaming->header.bTerminalLink == term->id) {
+                               dev->video.streaming = streaming;
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (found)
+                       break;
+       }
+
+       if (!found) {
+               uvc_printk(KERN_INFO, "No valid video chain found.\n");
+               return -1;
+       }
+
+       if (uvc_trace_param & UVC_TRACE_PROBE) {
+               uvc_printk(KERN_INFO, "Found a valid video chain (");
+               list_for_each_entry(term, &dev->video.iterms, chain) {
+                       printk("%d", term->id);
+                       if (term->chain.next != &dev->video.iterms)
+                               printk(",");
+               }
+               printk(" -> %d).\n", dev->video.oterm->id);
+       }
+
+       /* Initialize the streaming interface with default streaming
+        * parameters.
+        */
+       if ((ret = uvc_video_init(&dev->video)) < 0) {
+               uvc_printk(KERN_ERR, "Failed to initialize the device "
+                       "(%d).\n", ret);
+               return ret;
+       }
+
+       /* Register the device with V4L. */
+       vdev = video_device_alloc();
+       if (vdev == NULL)
+               return -1;
+
+       /* We already hold a reference to dev->udev. The video device will be
+        * unregistered before the reference is released, so we don't need to
+        * get another one.
+        */
+       vdev->dev = &dev->intf->dev;
+       vdev->type = 0;
+       vdev->type2 = 0;
+       vdev->minor = -1;
+       vdev->fops = &uvc_fops;
+       vdev->release = video_device_release;
+       strncpy(vdev->name, dev->name, sizeof vdev->name);
+
+       /* 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);
+
+       if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) {
+               dev->video.vdev = NULL;
+               video_device_release(vdev);
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+ * Delete the UVC device.
+ *
+ * Called by the kernel when the last reference to the uvc_device structure
+ * is released.
+ *
+ * Unregistering the video devices is done here because every opened instance
+ * must be closed before the device can be unregistered. An alternative would
+ * have been to use another reference count for uvc_v4l2_open/uvc_release, and
+ * unregister the video devices on disconnect when that reference count drops
+ * to zero.
+ *
+ * As this function is called after or during disconnect(), all URBs have
+ * already been canceled by the USB core. There is no need to kill the
+ * interrupt URB manually.
+ */
+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 */
+       uvc_unregister_video(dev);
+       usb_put_intf(dev->intf);
+       usb_put_dev(dev->udev);
+
+       uvc_status_cleanup(dev);
+       uvc_ctrl_cleanup_device(dev);
+
+       list_for_each_safe(p, n, &dev->entities) {
+               struct uvc_entity *entity;
+               entity = list_entry(p, struct uvc_entity, list);
+               kfree(entity);
+       }
+
+       list_for_each_safe(p, n, &dev->streaming) {
+               struct uvc_streaming *streaming;
+               streaming = list_entry(p, struct uvc_streaming, list);
+               usb_driver_release_interface(&uvc_driver.driver,
+                       streaming->intf);
+               usb_put_intf(streaming->intf);
+               kfree(streaming->format);
+               kfree(streaming->header.bmaControls);
+               kfree(streaming);
+       }
+
+       kfree(dev);
+}
+
+static int uvc_probe(struct usb_interface *intf,
+                    const struct usb_device_id *id)
+{
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct uvc_device *dev;
+       int ret;
+
+       if (id->idVendor && id->idProduct)
+               uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
+                               "(%04x:%04x)\n", udev->devpath, id->idVendor,
+                               id->idProduct);
+       else
+               uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
+                               udev->devpath);
+
+       /* Allocate memory for the device and initialize it */
+       if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&dev->entities);
+       INIT_LIST_HEAD(&dev->streaming);
+       kref_init(&dev->kref);
+
+       dev->udev = usb_get_dev(udev);
+       dev->intf = usb_get_intf(intf);
+       dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
+       dev->quirks = id->driver_info | uvc_quirks_param;
+
+       if (udev->product != NULL)
+               strncpy(dev->name, udev->product, sizeof dev->name);
+       else
+               snprintf(dev->name, sizeof dev->name,
+                       "UVC Camera (%04x:%04x)",
+                       le16_to_cpu(udev->descriptor.idVendor),
+                       le16_to_cpu(udev->descriptor.idProduct));
+
+       /* 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",
+               dev->uvc_version >> 8, dev->uvc_version & 0xff,
+               udev->product ? udev->product : "<unnamed>",
+               le16_to_cpu(udev->descriptor.idVendor),
+               le16_to_cpu(udev->descriptor.idProduct));
+
+       if (uvc_quirks_param != 0) {
+               uvc_printk(KERN_INFO, "Forcing device quirks 0x%x by module "
+                       "parameter for testing purpose.\n", uvc_quirks_param);
+               uvc_printk(KERN_INFO, "Please report required quirks to the "
+                       "linux-uvc-devel mailing list.\n");
+       }
+
+       /* Initialize controls */
+       if (uvc_ctrl_init_device(dev) < 0)
+               goto error;
+
+       /* Register the video devices */
+       if (uvc_register_video(dev) < 0)
+               goto error;
+
+       /* Save our data pointer in the interface data */
+       usb_set_intfdata(intf, dev);
+
+       /* 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 "
+                       "supported.\n", ret);
+       }
+
+       uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
+       return 0;
+
+error:
+       kref_put(&dev->kref, uvc_delete);
+       return -ENODEV;
+}
+
+static void uvc_disconnect(struct usb_interface *intf)
+{
+       struct uvc_device *dev = usb_get_intfdata(intf);
+
+       /* Set the USB interface data to NULL. This can be done outside the
+        * lock, as there's no other reader.
+        */
+       usb_set_intfdata(intf, NULL);
+
+       if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOSTREAMING)
+               return;
+
+       /* uvc_v4l2_open() might race uvc_disconnect(). A static driver-wide
+        * lock is needed to prevent uvc_disconnect from releasing its
+        * reference to the uvc_device instance after uvc_v4l2_open() received
+        * the pointer to the device (video_devdata) but before it got the
+        * chance to increase the reference count (kref_get).
+        */
+       mutex_lock(&uvc_driver.open_mutex);
+
+       dev->state |= UVC_DEV_DISCONNECTED;
+       kref_put(&dev->kref, uvc_delete);
+
+       mutex_unlock(&uvc_driver.open_mutex);
+}
+
+static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct uvc_device *dev = usb_get_intfdata(intf);
+
+       uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n",
+               intf->cur_altsetting->desc.bInterfaceNumber);
+
+       /* Controls are cached on the fly so they don't need to be saved. */
+       if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL)
+               return uvc_status_suspend(dev);
+
+       if (dev->video.streaming->intf != intf) {
+               uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB "
+                               "interface mismatch.\n");
+               return -EINVAL;
+       }
+
+       return uvc_video_suspend(&dev->video);
+}
+
+static int uvc_resume(struct usb_interface *intf)
+{
+       struct uvc_device *dev = usb_get_intfdata(intf);
+       int ret;
+
+       uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n",
+               intf->cur_altsetting->desc.bInterfaceNumber);
+
+       if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) {
+               if ((ret = uvc_ctrl_resume_device(dev)) < 0)
+                       return ret;
+
+               return uvc_status_resume(dev);
+       }
+
+       if (dev->video.streaming->intf != intf) {
+               uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB "
+                               "interface mismatch.\n");
+               return -EINVAL;
+       }
+
+       return uvc_video_resume(&dev->video);
+}
+
+/* ------------------------------------------------------------------------
+ * Driver initialization and cleanup
+ */
+
+/*
+ * The Logitech cameras listed below have their interface class set to
+ * VENDOR_SPEC because they don't announce themselves as UVC devices, even
+ * 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,
+         .idVendor             = 0x045e,
+         .idProduct            = 0x00f8,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
+       /* Microsoft Lifecam VX-7000 */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x045e,
+         .idProduct            = 0x0723,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
+       /* Logitech Quickcam Fusion */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x046d,
+         .idProduct            = 0x08c1,
+         .bInterfaceClass      = USB_CLASS_VENDOR_SPEC,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0 },
+       /* Logitech Quickcam Orbit MP */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x046d,
+         .idProduct            = 0x08c2,
+         .bInterfaceClass      = USB_CLASS_VENDOR_SPEC,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0 },
+       /* Logitech Quickcam Pro for Notebook */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x046d,
+         .idProduct            = 0x08c3,
+         .bInterfaceClass      = USB_CLASS_VENDOR_SPEC,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0 },
+       /* Logitech Quickcam Pro 5000 */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x046d,
+         .idProduct            = 0x08c5,
+         .bInterfaceClass      = USB_CLASS_VENDOR_SPEC,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0 },
+       /* Logitech Quickcam OEM Dell Notebook */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x046d,
+         .idProduct            = 0x08c6,
+         .bInterfaceClass      = USB_CLASS_VENDOR_SPEC,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0 },
+       /* Logitech Quickcam OEM Cisco VT Camera II */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x046d,
+         .idProduct            = 0x08c7,
+         .bInterfaceClass      = USB_CLASS_VENDOR_SPEC,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0 },
+       /* Apple Built-In iSight */
+       { .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,
+         .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,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
+       /* MT6227 */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x0e8d,
+         .idProduct            = 0x0004,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
+       /* Syntek (HP Spartan) */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x174f,
+         .idProduct            = 0x5212,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_STREAM_NO_FID },
+       /* Syntek (Asus U3S) */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x174f,
+         .idProduct            = 0x8a33,
+         .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,
+         .idVendor             = 0x18cd,
+         .idProduct            = 0xcafe,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_PROBE_EXTRAFIELDS },
+       /* Bodelin ProScopeHR */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_DEV_HI
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x19ab,
+         .idProduct            = 0x1000,
+         .bcdDevice_hi         = 0x0126,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .bInterfaceSubClass   = 1,
+         .bInterfaceProtocol   = 0,
+         .driver_info          = UVC_QUIRK_STATUS_INTERVAL },
+       /* SiGma Micro USB Web Camera */
+       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
+                               | USB_DEVICE_ID_MATCH_INT_INFO,
+         .idVendor             = 0x1c4f,
+         .idProduct            = 0x3000,
+         .bInterfaceClass      = USB_CLASS_VIDEO,
+         .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 */
+       { .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 */
+       { .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 },
+       /* Acer OrbiCam - Unknown vendor */
+       { .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 },
+       /* Generic USB Video Class */
+       { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
+       {}
+};
+
+MODULE_DEVICE_TABLE(usb, uvc_ids);
+
+struct uvc_driver uvc_driver = {
+       .driver = {
+               .name           = "uvcvideo",
+               .probe          = uvc_probe,
+               .disconnect     = uvc_disconnect,
+               .suspend        = uvc_suspend,
+               .resume         = uvc_resume,
+               .id_table       = uvc_ids,
+               .supports_autosuspend = 1,
+       },
+};
+
+static int __init uvc_init(void)
+{
+       int result;
+
+       INIT_LIST_HEAD(&uvc_driver.devices);
+       INIT_LIST_HEAD(&uvc_driver.controls);
+       mutex_init(&uvc_driver.open_mutex);
+       mutex_init(&uvc_driver.ctrl_mutex);
+
+       uvc_ctrl_init();
+
+       result = usb_register(&uvc_driver.driver);
+       if (result == 0)
+               printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
+       return result;
+}
+
+static void __exit uvc_cleanup(void)
+{
+       usb_deregister(&uvc_driver.driver);
+}
+
+module_init(uvc_init);
+module_exit(uvc_cleanup);
+
+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);
+MODULE_PARM_DESC(trace, "Trace level bitmask");
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/media/video/uvc/uvc_isight.c b/drivers/media/video/uvc/uvc_isight.c
new file mode 100644 (file)
index 0000000..37bdefd
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ *      uvc_isight.c  --  USB Video Class driver - iSight support
+ *
+ *     Copyright (C) 2006-2007
+ *             Ivan N. Zlatev <contact@i-nz.net>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ */
+
+#include <linux/usb.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+
+#include "uvcvideo.h"
+
+/* Built-in iSight webcams implements most of UVC 1.0 except a
+ * different packet format. Instead of sending a header at the
+ * beginning of each isochronous transfer payload, the webcam sends a
+ * single header per image (on its own in a packet), followed by
+ * packets containing data only.
+ *
+ * Offset   Size (bytes)       Description
+ * ------------------------------------------------------------------
+ * 0x00        1       Header length
+ * 0x01        1       Flags (UVC-compliant)
+ * 0x02        4       Always equal to '11223344'
+ * 0x06        8       Always equal to 'deadbeefdeadface'
+ * 0x0e        16      Unknown
+ *
+ * The header can be prefixed by an optional, unknown-purpose byte.
+ */
+
+static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
+               const __u8 *data, unsigned int len)
+{
+       static const __u8 hdr[] = {
+               0x11, 0x22, 0x33, 0x44,
+               0xde, 0xad, 0xbe, 0xef,
+               0xde, 0xad, 0xfa, 0xce
+       };
+
+       unsigned int maxlen, nbytes;
+       __u8 *mem;
+       int is_header = 0;
+
+       if (buf == NULL)
+               return 0;
+
+       if ((len >= 14 && memcmp(&data[2], hdr, 12) == 0) ||
+           (len >= 15 && memcmp(&data[3], hdr, 12) == 0)) {
+               uvc_trace(UVC_TRACE_FRAME, "iSight header found\n");
+               is_header = 1;
+       }
+
+       /* Synchronize to the input stream by waiting for a header packet. */
+       if (buf->state != UVC_BUF_STATE_ACTIVE) {
+               if (!is_header) {
+                       uvc_trace(UVC_TRACE_FRAME, "Dropping packet (out of "
+                                 "sync).\n");
+                       return 0;
+               }
+
+               buf->state = UVC_BUF_STATE_ACTIVE;
+       }
+
+       /* Mark the buffer as done if we're at the beginning of a new frame.
+        *
+        * Empty buffers (bytesused == 0) don't trigger end of frame detection
+        * as it doesn't make sense to return an empty buffer.
+        */
+       if (is_header && buf->buf.bytesused != 0) {
+               buf->state = UVC_BUF_STATE_DONE;
+               return -EAGAIN;
+       }
+
+       /* Copy the video data to the buffer. Skip header packets, as they
+        * contain no data.
+        */
+       if (!is_header) {
+               maxlen = buf->buf.length - buf->buf.bytesused;
+               mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused;
+               nbytes = min(len, maxlen);
+               memcpy(mem, data, nbytes);
+               buf->buf.bytesused += nbytes;
+
+               if (len > maxlen || buf->buf.bytesused == buf->buf.length) {
+                       uvc_trace(UVC_TRACE_FRAME, "Frame complete "
+                                 "(overflow).\n");
+                       buf->state = UVC_BUF_STATE_DONE;
+               }
+       }
+
+       return 0;
+}
+
+void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video,
+               struct uvc_buffer *buf)
+{
+       int ret, i;
+
+       for (i = 0; i < urb->number_of_packets; ++i) {
+               if (urb->iso_frame_desc[i].status < 0) {
+                       uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
+                                 "lost (%d).\n",
+                                 urb->iso_frame_desc[i].status);
+               }
+
+               /* Decode the payload packet.
+                * uvc_video_decode is entered twice when a frame transition
+                * has been detected because the end of frame can only be
+                * reliably detected when the first packet of the new frame
+                * is processed. The first pass detects the transition and
+                * closes the previous frame's buffer, the second pass
+                * processes the data of the first payload of the new frame.
+                */
+               do {
+                       ret = isight_decode(&video->queue, buf,
+                                       urb->transfer_buffer +
+                                       urb->iso_frame_desc[i].offset,
+                                       urb->iso_frame_desc[i].actual_length);
+
+                       if (buf == NULL)
+                               break;
+
+                       if (buf->state == UVC_BUF_STATE_DONE ||
+                           buf->state == UVC_BUF_STATE_ERROR)
+                               buf = uvc_queue_next_buffer(&video->queue, buf);
+               } while (ret == -EAGAIN);
+       }
+}
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c
new file mode 100644 (file)
index 0000000..0923f0e
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+ *      uvc_queue.c  --  USB Video Class driver - Buffers management
+ *
+ *      Copyright (C) 2005-2008
+ *          Laurent Pinchart (laurent.pinchart@skynet.be)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+
+#include "uvcvideo.h"
+
+/* ------------------------------------------------------------------------
+ * Video buffers queue management.
+ *
+ * Video queues is initialized by uvc_queue_init(). The function performs
+ * basic initialization of the uvc_video_queue struct and never fails.
+ *
+ * Video buffer allocation and freeing are performed by uvc_alloc_buffers and
+ * uvc_free_buffers respectively. The former acquires the video queue lock,
+ * while the later must be called with the lock held (so that allocation can
+ * free previously allocated buffers). Trying to free buffers that are mapped
+ * to user space will return -EBUSY.
+ *
+ * Video buffers are managed using two queues. However, unlike most USB video
+ * drivers which use an in queue and an out queue, we use a main queue which
+ * holds all queued buffers (both 'empty' and 'done' buffers), and an irq
+ * queue which holds empty buffers. This design (copied from video-buf)
+ * minimizes locking in interrupt, as only one queue is shared between
+ * interrupt and user contexts.
+ *
+ * Use cases
+ * ---------
+ *
+ * Unless stated otherwise, all operations which modify the irq buffers queue
+ * are protected by the irq spinlock.
+ *
+ * 1. The user queues the buffers, starts streaming and dequeues a buffer.
+ *
+ *    The buffers are added to the main and irq queues. Both operations are
+ *    protected by the queue lock, and the latert is protected by the irq
+ *    spinlock as well.
+ *
+ *    The completion handler fetches a buffer from the irq queue and fills it
+ *    with video data. If no buffer is available (irq queue empty), the handler
+ *    returns immediately.
+ *
+ *    When the buffer is full, the completion handler removes it from the irq
+ *    queue, marks it as ready (UVC_BUF_STATE_DONE) and wake its wait queue.
+ *    At that point, any process waiting on the buffer will be woken up. If a
+ *    process tries to dequeue a buffer after it has been marked ready, the
+ *    dequeing will succeed immediately.
+ *
+ * 2. Buffers are queued, user is waiting on a buffer and the device gets
+ *    disconnected.
+ *
+ *    When the device is disconnected, the kernel calls the completion handler
+ *    with an appropriate status code. The handler marks all buffers in the
+ *    irq queue as being erroneous (UVC_BUF_STATE_ERROR) and wakes them up so
+ *    that any process waiting on a buffer gets woken up.
+ *
+ *    Waking up up the first buffer on the irq list is not enough, as the
+ *    process waiting on the buffer might restart the dequeue operation
+ *    immediately.
+ *
+ */
+
+void uvc_queue_init(struct uvc_video_queue *queue)
+{
+       mutex_init(&queue->mutex);
+       spin_lock_init(&queue->irqlock);
+       INIT_LIST_HEAD(&queue->mainqueue);
+       INIT_LIST_HEAD(&queue->irqqueue);
+}
+
+/*
+ * Allocate the video buffers.
+ *
+ * Pages are reserved to make sure they will not be swaped, as they will be
+ * filled in URB completion handler.
+ *
+ * Buffers will be individually mapped, so they must all be page aligned.
+ */
+int uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers,
+               unsigned int buflength)
+{
+       unsigned int bufsize = PAGE_ALIGN(buflength);
+       unsigned int i;
+       void *mem = NULL;
+       int ret;
+
+       if (nbuffers > UVC_MAX_VIDEO_BUFFERS)
+               nbuffers = UVC_MAX_VIDEO_BUFFERS;
+
+       mutex_lock(&queue->mutex);
+
+       if ((ret = uvc_free_buffers(queue)) < 0)
+               goto done;
+
+       /* Bail out if no buffers should be allocated. */
+       if (nbuffers == 0)
+               goto done;
+
+       /* Decrement the number of buffers until allocation succeeds. */
+       for (; nbuffers > 0; --nbuffers) {
+               mem = vmalloc_32(nbuffers * bufsize);
+               if (mem != NULL)
+                       break;
+       }
+
+       if (mem == NULL) {
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       for (i = 0; i < nbuffers; ++i) {
+               memset(&queue->buffer[i], 0, sizeof queue->buffer[i]);
+               queue->buffer[i].buf.index = i;
+               queue->buffer[i].buf.m.offset = i * bufsize;
+               queue->buffer[i].buf.length = buflength;
+               queue->buffer[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               queue->buffer[i].buf.sequence = 0;
+               queue->buffer[i].buf.field = V4L2_FIELD_NONE;
+               queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP;
+               queue->buffer[i].buf.flags = 0;
+               init_waitqueue_head(&queue->buffer[i].wait);
+       }
+
+       queue->mem = mem;
+       queue->count = nbuffers;
+       queue->buf_size = bufsize;
+       ret = nbuffers;
+
+done:
+       mutex_unlock(&queue->mutex);
+       return ret;
+}
+
+/*
+ * Free the video buffers.
+ *
+ * This function must be called with the queue lock held.
+ */
+int uvc_free_buffers(struct uvc_video_queue *queue)
+{
+       unsigned int i;
+
+       for (i = 0; i < queue->count; ++i) {
+               if (queue->buffer[i].vma_use_count != 0)
+                       return -EBUSY;
+       }
+
+       if (queue->count) {
+               vfree(queue->mem);
+               queue->count = 0;
+       }
+
+       return 0;
+}
+
+static void __uvc_query_buffer(struct uvc_buffer *buf,
+               struct v4l2_buffer *v4l2_buf)
+{
+       memcpy(v4l2_buf, &buf->buf, sizeof *v4l2_buf);
+
+       if (buf->vma_use_count)
+               v4l2_buf->flags |= V4L2_BUF_FLAG_MAPPED;
+
+       switch (buf->state) {
+       case UVC_BUF_STATE_ERROR:
+       case UVC_BUF_STATE_DONE:
+               v4l2_buf->flags |= V4L2_BUF_FLAG_DONE;
+               break;
+       case UVC_BUF_STATE_QUEUED:
+       case UVC_BUF_STATE_ACTIVE:
+               v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED;
+               break;
+       case UVC_BUF_STATE_IDLE:
+       default:
+               break;
+       }
+}
+
+int uvc_query_buffer(struct uvc_video_queue *queue,
+               struct v4l2_buffer *v4l2_buf)
+{
+       int ret = 0;
+
+       mutex_lock(&queue->mutex);
+       if (v4l2_buf->index >= queue->count) {
+               ret = -EINVAL;
+               goto done;
+       }
+
+       __uvc_query_buffer(&queue->buffer[v4l2_buf->index], v4l2_buf);
+
+done:
+       mutex_unlock(&queue->mutex);
+       return ret;
+}
+
+/*
+ * Queue a video buffer. Attempting to queue a buffer that has already been
+ * queued will return -EINVAL.
+ */
+int uvc_queue_buffer(struct uvc_video_queue *queue,
+       struct v4l2_buffer *v4l2_buf)
+{
+       struct uvc_buffer *buf;
+       unsigned long flags;
+       int ret = 0;
+
+       uvc_trace(UVC_TRACE_CAPTURE, "Queuing buffer %u.\n", v4l2_buf->index);
+
+       if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+           v4l2_buf->memory != V4L2_MEMORY_MMAP) {
+               uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
+                       "and/or memory (%u).\n", v4l2_buf->type,
+                       v4l2_buf->memory);
+               return -EINVAL;
+       }
+
+       mutex_lock(&queue->mutex);
+       if (v4l2_buf->index >= queue->count)  {
+               uvc_trace(UVC_TRACE_CAPTURE, "[E] Out of range index.\n");
+               ret = -EINVAL;
+               goto done;
+       }
+
+       buf = &queue->buffer[v4l2_buf->index];
+       if (buf->state != UVC_BUF_STATE_IDLE) {
+               uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state "
+                       "(%u).\n", buf->state);
+               ret = -EINVAL;
+               goto done;
+       }
+
+       spin_lock_irqsave(&queue->irqlock, flags);
+       if (queue->flags & UVC_QUEUE_DISCONNECTED) {
+               spin_unlock_irqrestore(&queue->irqlock, flags);
+               ret = -ENODEV;
+               goto done;
+       }
+       buf->state = UVC_BUF_STATE_QUEUED;
+       buf->buf.bytesused = 0;
+       list_add_tail(&buf->stream, &queue->mainqueue);
+       list_add_tail(&buf->queue, &queue->irqqueue);
+       spin_unlock_irqrestore(&queue->irqlock, flags);
+
+done:
+       mutex_unlock(&queue->mutex);
+       return ret;
+}
+
+static int uvc_queue_waiton(struct uvc_buffer *buf, int nonblocking)
+{
+       if (nonblocking) {
+               return (buf->state != UVC_BUF_STATE_QUEUED &&
+                       buf->state != UVC_BUF_STATE_ACTIVE)
+                       ? 0 : -EAGAIN;
+       }
+
+       return wait_event_interruptible(buf->wait,
+               buf->state != UVC_BUF_STATE_QUEUED &&
+               buf->state != UVC_BUF_STATE_ACTIVE);
+}
+
+/*
+ * Dequeue a video buffer. If nonblocking is false, block until a buffer is
+ * available.
+ */
+int uvc_dequeue_buffer(struct uvc_video_queue *queue,
+               struct v4l2_buffer *v4l2_buf, int nonblocking)
+{
+       struct uvc_buffer *buf;
+       int ret = 0;
+
+       if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+           v4l2_buf->memory != V4L2_MEMORY_MMAP) {
+               uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
+                       "and/or memory (%u).\n", v4l2_buf->type,
+                       v4l2_buf->memory);
+               return -EINVAL;
+       }
+
+       mutex_lock(&queue->mutex);
+       if (list_empty(&queue->mainqueue)) {
+               uvc_trace(UVC_TRACE_CAPTURE, "[E] Empty buffer queue.\n");
+               ret = -EINVAL;
+               goto done;
+       }
+
+       buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream);
+       if ((ret = uvc_queue_waiton(buf, nonblocking)) < 0)
+               goto done;
+
+       uvc_trace(UVC_TRACE_CAPTURE, "Dequeuing buffer %u (%u, %u bytes).\n",
+               buf->buf.index, buf->state, buf->buf.bytesused);
+
+       switch (buf->state) {
+       case UVC_BUF_STATE_ERROR:
+               uvc_trace(UVC_TRACE_CAPTURE, "[W] Corrupted data "
+                       "(transmission error).\n");
+               ret = -EIO;
+       case UVC_BUF_STATE_DONE:
+               buf->state = UVC_BUF_STATE_IDLE;
+               break;
+
+       case UVC_BUF_STATE_IDLE:
+       case UVC_BUF_STATE_QUEUED:
+       case UVC_BUF_STATE_ACTIVE:
+       default:
+               uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state %u "
+                       "(driver bug?).\n", buf->state);
+               ret = -EINVAL;
+               goto done;
+       }
+
+       list_del(&buf->stream);
+       __uvc_query_buffer(buf, v4l2_buf);
+
+done:
+       mutex_unlock(&queue->mutex);
+       return ret;
+}
+
+/*
+ * Poll the video queue.
+ *
+ * This function implements video queue polling and is intended to be used by
+ * the device poll handler.
+ */
+unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
+               poll_table *wait)
+{
+       struct uvc_buffer *buf;
+       unsigned int mask = 0;
+
+       mutex_lock(&queue->mutex);
+       if (list_empty(&queue->mainqueue)) {
+               mask |= POLLERR;
+               goto done;
+       }
+       buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream);
+
+       poll_wait(file, &buf->wait, wait);
+       if (buf->state == UVC_BUF_STATE_DONE ||
+           buf->state == UVC_BUF_STATE_ERROR)
+               mask |= POLLIN | POLLRDNORM;
+
+done:
+       mutex_unlock(&queue->mutex);
+       return mask;
+}
+
+/*
+ * Enable or disable the video buffers queue.
+ *
+ * The queue must be enabled before starting video acquisition and must be
+ * disabled after stopping it. This ensures that the video buffers queue
+ * state can be properly initialized before buffers are accessed from the
+ * interrupt handler.
+ *
+ * Enabling the video queue initializes parameters (such as sequence number,
+ * sync pattern, ...). If the queue is already enabled, return -EBUSY.
+ *
+ * Disabling the video queue cancels the queue and removes all buffers from
+ * the main queue.
+ *
+ * This function can't be called from interrupt context. Use
+ * uvc_queue_cancel() instead.
+ */
+int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
+{
+       unsigned int i;
+       int ret = 0;
+
+       mutex_lock(&queue->mutex);
+       if (enable) {
+               if (uvc_queue_streaming(queue)) {
+                       ret = -EBUSY;
+                       goto done;
+               }
+               queue->sequence = 0;
+               queue->flags |= UVC_QUEUE_STREAMING;
+       } else {
+               uvc_queue_cancel(queue, 0);
+               INIT_LIST_HEAD(&queue->mainqueue);
+
+               for (i = 0; i < queue->count; ++i)
+                       queue->buffer[i].state = UVC_BUF_STATE_IDLE;
+
+               queue->flags &= ~UVC_QUEUE_STREAMING;
+       }
+
+done:
+       mutex_unlock(&queue->mutex);
+       return ret;
+}
+
+/*
+ * Cancel the video buffers queue.
+ *
+ * Cancelling the queue marks all buffers on the irq queue as erroneous,
+ * wakes them up and remove them from the queue.
+ *
+ * If the disconnect parameter is set, further calls to uvc_queue_buffer will
+ * fail with -ENODEV.
+ *
+ * This function acquires the irq spinlock and can be called from interrupt
+ * context.
+ */
+void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
+{
+       struct uvc_buffer *buf;
+       unsigned long flags;
+
+       spin_lock_irqsave(&queue->irqlock, flags);
+       while (!list_empty(&queue->irqqueue)) {
+               buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
+                                      queue);
+               list_del(&buf->queue);
+               buf->state = UVC_BUF_STATE_ERROR;
+               wake_up(&buf->wait);
+       }
+       /* This must be protected by the irqlock spinlock to avoid race
+        * conditions between uvc_queue_buffer and the disconnection event that
+        * could result in an interruptible wait in uvc_dequeue_buffer. Do not
+        * blindly replace this logic by checking for the UVC_DEV_DISCONNECTED
+        * state outside the queue code.
+        */
+       if (disconnect)
+               queue->flags |= UVC_QUEUE_DISCONNECTED;
+       spin_unlock_irqrestore(&queue->irqlock, flags);
+}
+
+struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
+               struct uvc_buffer *buf)
+{
+       struct uvc_buffer *nextbuf;
+       unsigned long flags;
+
+       if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
+           buf->buf.length != buf->buf.bytesused) {
+               buf->state = UVC_BUF_STATE_QUEUED;
+               buf->buf.bytesused = 0;
+               return buf;
+       }
+
+       spin_lock_irqsave(&queue->irqlock, flags);
+       list_del(&buf->queue);
+       if (!list_empty(&queue->irqqueue))
+               nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
+                                          queue);
+       else
+               nextbuf = NULL;
+       spin_unlock_irqrestore(&queue->irqlock, flags);
+
+       buf->buf.sequence = queue->sequence++;
+       do_gettimeofday(&buf->buf.timestamp);
+
+       wake_up(&buf->wait);
+       return nextbuf;
+}
diff --git a/drivers/media/video/uvc/uvc_status.c b/drivers/media/video/uvc/uvc_status.c
new file mode 100644 (file)
index 0000000..be9084e
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ *      uvc_status.c  --  USB Video Class driver - Status endpoint
+ *
+ *      Copyright (C) 2007-2008
+ *          Laurent Pinchart (laurent.pinchart@skynet.be)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+
+#include "uvcvideo.h"
+
+/* --------------------------------------------------------------------------
+ * Input device
+ */
+static int uvc_input_init(struct uvc_device *dev)
+{
+       struct usb_device *udev = dev->udev;
+       struct input_dev *input;
+       char *phys = NULL;
+       int ret;
+
+       input = input_allocate_device();
+       if (input == NULL)
+               return -ENOMEM;
+
+       phys = kmalloc(6 + strlen(udev->bus->bus_name) + strlen(udev->devpath),
+                       GFP_KERNEL);
+       if (phys == NULL) {
+               ret = -ENOMEM;
+               goto error;
+       }
+       sprintf(phys, "usb-%s-%s", udev->bus->bus_name, udev->devpath);
+
+       input->name = dev->name;
+       input->phys = phys;
+       usb_to_input_id(udev, &input->id);
+       input->dev.parent = &dev->intf->dev;
+
+       set_bit(EV_KEY, input->evbit);
+       set_bit(BTN_0, input->keybit);
+
+       if ((ret = input_register_device(input)) < 0)
+               goto error;
+
+       dev->input = input;
+       return 0;
+
+error:
+       input_free_device(input);
+       kfree(phys);
+       return ret;
+}
+
+static void uvc_input_cleanup(struct uvc_device *dev)
+{
+       if (dev->input)
+               input_unregister_device(dev->input);
+}
+
+/* --------------------------------------------------------------------------
+ * Status interrupt endpoint
+ */
+static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len)
+{
+       if (len < 3) {
+               uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event "
+                               "received.\n");
+               return;
+       }
+
+       if (data[2] == 0) {
+               if (len < 4)
+                       return;
+               uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n",
+                       data[1], data[3] ? "pressed" : "released", len);
+               if (dev->input)
+                       input_report_key(dev->input, BTN_0, data[3]);
+       } else {
+               uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x %02x "
+                       "len %d.\n", data[1], data[2], data[3], len);
+       }
+}
+
+static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len)
+{
+       char *attrs[3] = { "value", "info", "failure" };
+
+       if (len < 6 || data[2] != 0 || data[4] > 2) {
+               uvc_trace(UVC_TRACE_STATUS, "Invalid control status event "
+                               "received.\n");
+               return;
+       }
+
+       uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n",
+               data[1], data[3], attrs[data[4]], len);
+}
+
+static void uvc_status_complete(struct urb *urb)
+{
+       struct uvc_device *dev = urb->context;
+       int len, ret;
+
+       switch (urb->status) {
+       case 0:
+               break;
+
+       case -ENOENT:           /* usb_kill_urb() called. */
+       case -ECONNRESET:       /* usb_unlink_urb() called. */
+       case -ESHUTDOWN:        /* The endpoint is being disabled. */
+       case -EPROTO:           /* Device is disconnected (reported by some
+                                * host controller). */
+               return;
+
+       default:
+               uvc_printk(KERN_WARNING, "Non-zero status (%d) in status "
+                       "completion handler.\n", urb->status);
+               return;
+       }
+
+       len = urb->actual_length;
+       if (len > 0) {
+               switch (dev->status[0] & 0x0f) {
+               case UVC_STATUS_TYPE_CONTROL:
+                       uvc_event_control(dev, dev->status, len);
+                       break;
+
+               case UVC_STATUS_TYPE_STREAMING:
+                       uvc_event_streaming(dev, dev->status, len);
+                       break;
+
+               default:
+                       uvc_printk(KERN_INFO, "unknown event type %u.\n",
+                               dev->status[0]);
+                       break;
+               }
+       }
+
+       /* Resubmit the URB. */
+       urb->interval = dev->int_ep->desc.bInterval;
+       if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+               uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n",
+                       ret);
+       }
+}
+
+int uvc_status_init(struct uvc_device *dev)
+{
+       struct usb_host_endpoint *ep = dev->int_ep;
+       unsigned int pipe;
+       int interval;
+
+       if (ep == NULL)
+               return 0;
+
+       uvc_input_init(dev);
+
+       dev->int_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (dev->int_urb == NULL)
+               return -ENOMEM;
+
+       pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress);
+
+       /* For high-speed interrupt endpoints, the bInterval value is used as
+        * an exponent of two. Some developers forgot about it.
+        */
+       interval = ep->desc.bInterval;
+       if (interval > 16 && dev->udev->speed == USB_SPEED_HIGH &&
+           (dev->quirks & UVC_QUIRK_STATUS_INTERVAL))
+               interval = fls(interval) - 1;
+
+       usb_fill_int_urb(dev->int_urb, dev->udev, pipe,
+               dev->status, sizeof dev->status, uvc_status_complete,
+               dev, interval);
+
+       return usb_submit_urb(dev->int_urb, GFP_KERNEL);
+}
+
+void uvc_status_cleanup(struct uvc_device *dev)
+{
+       usb_kill_urb(dev->int_urb);
+       usb_free_urb(dev->int_urb);
+       uvc_input_cleanup(dev);
+}
+
+int uvc_status_suspend(struct uvc_device *dev)
+{
+       usb_kill_urb(dev->int_urb);
+       return 0;
+}
+
+int uvc_status_resume(struct uvc_device *dev)
+{
+       if (dev->int_urb == NULL)
+               return 0;
+
+       return usb_submit_urb(dev->int_urb, GFP_KERNEL);
+}
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
new file mode 100644 (file)
index 0000000..2e0a665
--- /dev/null
@@ -0,0 +1,1105 @@
+/*
+ *      uvc_v4l2.c  --  USB Video Class driver - V4L2 API
+ *
+ *      Copyright (C) 2005-2008
+ *          Laurent Pinchart (laurent.pinchart@skynet.be)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+
+#include <media/v4l2-common.h>
+
+#include "uvcvideo.h"
+
+/* ------------------------------------------------------------------------
+ * V4L2 interface
+ */
+
+/*
+ * Mapping V4L2 controls to UVC controls can be straighforward if done well.
+ * Most of the UVC controls exist in V4L2, and can be mapped directly. Some
+ * must be grouped (for instance the Red Balance, Blue Balance and Do White
+ * Balance V4L2 controls use the White Balance Component UVC control) or
+ * otherwise translated. The approach we take here is to use a translation
+ * table for the controls which can be mapped directly, and handle the others
+ * manually.
+ */
+static int uvc_v4l2_query_menu(struct uvc_video_device *video,
+       struct v4l2_querymenu *query_menu)
+{
+       struct uvc_menu_info *menu_info;
+       struct uvc_control_mapping *mapping;
+       struct uvc_control *ctrl;
+
+       ctrl = uvc_find_control(video, query_menu->id, &mapping);
+       if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU)
+               return -EINVAL;
+
+       if (query_menu->index >= mapping->menu_count)
+               return -EINVAL;
+
+       menu_info = &mapping->menu_info[query_menu->index];
+       strncpy(query_menu->name, menu_info->name, 32);
+       return 0;
+}
+
+/*
+ * Find the frame interval closest to the requested frame interval for the
+ * given frame format and size. This should be done by the device as part of
+ * the Video Probe and Commit negotiation, but some hardware don't implement
+ * that feature.
+ */
+static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval)
+{
+       unsigned int i;
+
+       if (frame->bFrameIntervalType) {
+               __u32 best = -1, dist;
+
+               for (i = 0; i < frame->bFrameIntervalType; ++i) {
+                       dist = interval > frame->dwFrameInterval[i]
+                            ? interval - frame->dwFrameInterval[i]
+                            : frame->dwFrameInterval[i] - interval;
+
+                       if (dist > best)
+                               break;
+
+                       best = dist;
+               }
+
+               interval = frame->dwFrameInterval[i-1];
+       } else {
+               const __u32 min = frame->dwFrameInterval[0];
+               const __u32 max = frame->dwFrameInterval[1];
+               const __u32 step = frame->dwFrameInterval[2];
+
+               interval = min + (interval - min + step/2) / step * step;
+               if (interval > max)
+                       interval = max;
+       }
+
+       return interval;
+}
+
+static int uvc_v4l2_try_format(struct uvc_video_device *video,
+       struct v4l2_format *fmt, struct uvc_streaming_control *probe,
+       struct uvc_format **uvc_format, struct uvc_frame **uvc_frame)
+{
+       struct uvc_format *format = NULL;
+       struct uvc_frame *frame = NULL;
+       __u16 rw, rh;
+       unsigned int d, maxd;
+       unsigned int i;
+       __u32 interval;
+       int ret = 0;
+       __u8 *fcc;
+
+       if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       fcc = (__u8 *)&fmt->fmt.pix.pixelformat;
+       uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n",
+                       fmt->fmt.pix.pixelformat,
+                       fcc[0], fcc[1], fcc[2], fcc[3],
+                       fmt->fmt.pix.width, fmt->fmt.pix.height);
+
+       /* Check if the hardware supports the requested format. */
+       for (i = 0; i < video->streaming->nformats; ++i) {
+               format = &video->streaming->format[i];
+               if (format->fcc == fmt->fmt.pix.pixelformat)
+                       break;
+       }
+
+       if (format == NULL || format->fcc != fmt->fmt.pix.pixelformat) {
+               uvc_trace(UVC_TRACE_FORMAT, "Unsupported format 0x%08x.\n",
+                               fmt->fmt.pix.pixelformat);
+               return -EINVAL;
+       }
+
+       /* Find the closest image size. The distance between image sizes is
+        * the size in pixels of the non-overlapping regions between the
+        * requested size and the frame-specified size.
+        */
+       rw = fmt->fmt.pix.width;
+       rh = fmt->fmt.pix.height;
+       maxd = (unsigned int)-1;
+
+       for (i = 0; i < format->nframes; ++i) {
+               __u16 w = format->frame[i].wWidth;
+               __u16 h = format->frame[i].wHeight;
+
+               d = min(w, rw) * min(h, rh);
+               d = w*h + rw*rh - 2*d;
+               if (d < maxd) {
+                       maxd = d;
+                       frame = &format->frame[i];
+               }
+
+               if (maxd == 0)
+                       break;
+       }
+
+       if (frame == NULL) {
+               uvc_trace(UVC_TRACE_FORMAT, "Unsupported size %ux%u.\n",
+                               fmt->fmt.pix.width, fmt->fmt.pix.height);
+               return -EINVAL;
+       }
+
+       /* Use the default frame interval. */
+       interval = frame->dwDefaultFrameInterval;
+       uvc_trace(UVC_TRACE_FORMAT, "Using default frame interval %u.%u us "
+               "(%u.%u fps).\n", interval/10, interval%10, 10000000/interval,
+               (100000000/interval)%10);
+
+       /* Set the format index, frame index and frame interval. */
+       memset(probe, 0, sizeof *probe);
+       probe->bmHint = 1;      /* dwFrameInterval */
+       probe->bFormatIndex = format->index;
+       probe->bFrameIndex = frame->bFrameIndex;
+       probe->dwFrameInterval = uvc_try_frame_interval(frame, interval);
+       /* Some webcams stall the probe control set request when the
+        * dwMaxVideoFrameSize field is set to zero. The UVC specification
+        * clearly states that the field is read-only from the host, so this
+        * is a webcam bug. Set dwMaxVideoFrameSize to the value reported by
+        * the webcam to work around the problem.
+        *
+        * The workaround could probably be enabled for all webcams, so the
+        * quirk can be removed if needed. It's currently useful to detect
+        * webcam bugs and fix them before they hit the market (providing
+        * developers test their webcams with the Linux driver as well as with
+        * the Windows driver).
+        */
+       if (video->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS)
+               probe->dwMaxVideoFrameSize =
+                       video->streaming->ctrl.dwMaxVideoFrameSize;
+
+       /* Probe the device */
+       if ((ret = uvc_probe_video(video, probe)) < 0)
+               goto done;
+
+       fmt->fmt.pix.width = frame->wWidth;
+       fmt->fmt.pix.height = frame->wHeight;
+       fmt->fmt.pix.field = V4L2_FIELD_NONE;
+       fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8;
+       fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize;
+       fmt->fmt.pix.colorspace = format->colorspace;
+       fmt->fmt.pix.priv = 0;
+
+       if (uvc_format != NULL)
+               *uvc_format = format;
+       if (uvc_frame != NULL)
+               *uvc_frame = frame;
+
+done:
+       return ret;
+}
+
+static int uvc_v4l2_get_format(struct uvc_video_device *video,
+       struct v4l2_format *fmt)
+{
+       struct uvc_format *format = video->streaming->cur_format;
+       struct uvc_frame *frame = video->streaming->cur_frame;
+
+       if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       if (format == NULL || frame == NULL)
+               return -EINVAL;
+
+       fmt->fmt.pix.pixelformat = format->fcc;
+       fmt->fmt.pix.width = frame->wWidth;
+       fmt->fmt.pix.height = frame->wHeight;
+       fmt->fmt.pix.field = V4L2_FIELD_NONE;
+       fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8;
+       fmt->fmt.pix.sizeimage = video->streaming->ctrl.dwMaxVideoFrameSize;
+       fmt->fmt.pix.colorspace = format->colorspace;
+       fmt->fmt.pix.priv = 0;
+
+       return 0;
+}
+
+static int uvc_v4l2_set_format(struct uvc_video_device *video,
+       struct v4l2_format *fmt)
+{
+       struct uvc_streaming_control probe;
+       struct uvc_format *format;
+       struct uvc_frame *frame;
+       int ret;
+
+       if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       if (uvc_queue_streaming(&video->queue))
+               return -EBUSY;
+
+       ret = uvc_v4l2_try_format(video, fmt, &probe, &format, &frame);
+       if (ret < 0)
+               return ret;
+
+       if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0)
+               return ret;
+
+       memcpy(&video->streaming->ctrl, &probe, sizeof probe);
+       video->streaming->cur_format = format;
+       video->streaming->cur_frame = frame;
+
+       return 0;
+}
+
+static int uvc_v4l2_get_streamparm(struct uvc_video_device *video,
+               struct v4l2_streamparm *parm)
+{
+       uint32_t numerator, denominator;
+
+       if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       numerator = video->streaming->ctrl.dwFrameInterval;
+       denominator = 10000000;
+       uvc_simplify_fraction(&numerator, &denominator, 8, 333);
+
+       memset(parm, 0, sizeof *parm);
+       parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+       parm->parm.capture.capturemode = 0;
+       parm->parm.capture.timeperframe.numerator = numerator;
+       parm->parm.capture.timeperframe.denominator = denominator;
+       parm->parm.capture.extendedmode = 0;
+       parm->parm.capture.readbuffers = 0;
+
+       return 0;
+}
+
+static int uvc_v4l2_set_streamparm(struct uvc_video_device *video,
+               struct v4l2_streamparm *parm)
+{
+       struct uvc_frame *frame = video->streaming->cur_frame;
+       struct uvc_streaming_control probe;
+       uint32_t interval;
+       int ret;
+
+       if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       if (uvc_queue_streaming(&video->queue))
+               return -EBUSY;
+
+       memcpy(&probe, &video->streaming->ctrl, sizeof probe);
+       interval = uvc_fraction_to_interval(
+                       parm->parm.capture.timeperframe.numerator,
+                       parm->parm.capture.timeperframe.denominator);
+
+       uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n",
+                       parm->parm.capture.timeperframe.numerator,
+                       parm->parm.capture.timeperframe.denominator,
+                       interval);
+       probe.dwFrameInterval = uvc_try_frame_interval(frame, interval);
+
+       /* Probe the device with the new settings. */
+       if ((ret = uvc_probe_video(video, &probe)) < 0)
+               return ret;
+
+       /* Commit the new settings. */
+       if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0)
+               return ret;
+
+       memcpy(&video->streaming->ctrl, &probe, sizeof probe);
+
+       /* Return the actual frame period. */
+       parm->parm.capture.timeperframe.numerator = probe.dwFrameInterval;
+       parm->parm.capture.timeperframe.denominator = 10000000;
+       uvc_simplify_fraction(&parm->parm.capture.timeperframe.numerator,
+                               &parm->parm.capture.timeperframe.denominator,
+                               8, 333);
+
+       return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * Privilege management
+ */
+
+/*
+ * Privilege management is the multiple-open implementation basis. The current
+ * implementation is completely transparent for the end-user and doesn't
+ * require explicit use of the VIDIOC_G_PRIORITY and VIDIOC_S_PRIORITY ioctls.
+ * Those ioctls enable finer control on the device (by making possible for a
+ * user to request exclusive access to a device), but are not mature yet.
+ * Switching to the V4L2 priority mechanism might be considered in the future
+ * if this situation changes.
+ *
+ * Each open instance of a UVC device can either be in a privileged or
+ * unprivileged state. Only a single instance can be in a privileged state at
+ * a given time. Trying to perform an operation which requires privileges will
+ * automatically acquire the required privileges if possible, or return -EBUSY
+ * otherwise. Privileges are dismissed when closing the instance.
+ *
+ * Operations which require privileges are:
+ *
+ * - VIDIOC_S_INPUT
+ * - VIDIOC_S_PARM
+ * - VIDIOC_S_FMT
+ * - VIDIOC_TRY_FMT
+ * - VIDIOC_REQBUFS
+ */
+static int uvc_acquire_privileges(struct uvc_fh *handle)
+{
+       int ret = 0;
+
+       /* Always succeed if the handle is already privileged. */
+       if (handle->state == UVC_HANDLE_ACTIVE)
+               return 0;
+
+       /* Check if the device already has a privileged handle. */
+       mutex_lock(&uvc_driver.open_mutex);
+       if (atomic_inc_return(&handle->device->active) != 1) {
+               atomic_dec(&handle->device->active);
+               ret = -EBUSY;
+               goto done;
+       }
+
+       handle->state = UVC_HANDLE_ACTIVE;
+
+done:
+       mutex_unlock(&uvc_driver.open_mutex);
+       return ret;
+}
+
+static void uvc_dismiss_privileges(struct uvc_fh *handle)
+{
+       if (handle->state == UVC_HANDLE_ACTIVE)
+               atomic_dec(&handle->device->active);
+
+       handle->state = UVC_HANDLE_PASSIVE;
+}
+
+static int uvc_has_privileges(struct uvc_fh *handle)
+{
+       return handle->state == UVC_HANDLE_ACTIVE;
+}
+
+/* ------------------------------------------------------------------------
+ * V4L2 file operations
+ */
+
+static int uvc_v4l2_open(struct inode *inode, struct file *file)
+{
+       struct video_device *vdev;
+       struct uvc_video_device *video;
+       struct uvc_fh *handle;
+       int ret = 0;
+
+       uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n");
+       mutex_lock(&uvc_driver.open_mutex);
+       vdev = video_devdata(file);
+       video = video_get_drvdata(vdev);
+
+       if (video->dev->state & UVC_DEV_DISCONNECTED) {
+               ret = -ENODEV;
+               goto done;
+       }
+
+       ret = usb_autopm_get_interface(video->dev->intf);
+       if (ret < 0)
+               goto done;
+
+       /* Create the device handle. */
+       handle = kzalloc(sizeof *handle, GFP_KERNEL);
+       if (handle == NULL) {
+               usb_autopm_put_interface(video->dev->intf);
+               ret = -ENOMEM;
+               goto done;
+       }
+
+       handle->device = video;
+       handle->state = UVC_HANDLE_PASSIVE;
+       file->private_data = handle;
+
+       kref_get(&video->dev->kref);
+
+done:
+       mutex_unlock(&uvc_driver.open_mutex);
+       return ret;
+}
+
+static int uvc_v4l2_release(struct inode *inode, struct file *file)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct uvc_video_device *video = video_get_drvdata(vdev);
+       struct uvc_fh *handle = (struct uvc_fh *)file->private_data;
+
+       uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n");
+
+       /* Only free resources if this is a privileged handle. */
+       if (uvc_has_privileges(handle)) {
+               uvc_video_enable(video, 0);
+
+               mutex_lock(&video->queue.mutex);
+               if (uvc_free_buffers(&video->queue) < 0)
+                       uvc_printk(KERN_ERR, "uvc_v4l2_release: Unable to "
+                                       "free buffers.\n");
+               mutex_unlock(&video->queue.mutex);
+       }
+
+       /* Release the file handle. */
+       uvc_dismiss_privileges(handle);
+       kfree(handle);
+       file->private_data = NULL;
+
+       usb_autopm_put_interface(video->dev->intf);
+       kref_put(&video->dev->kref, uvc_delete);
+       return 0;
+}
+
+static int uvc_v4l2_do_ioctl(struct inode *inode, struct file *file,
+                    unsigned int cmd, void *arg)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct uvc_video_device *video = video_get_drvdata(vdev);
+       struct uvc_fh *handle = (struct uvc_fh *)file->private_data;
+       int ret = 0;
+
+       if (uvc_trace_param & UVC_TRACE_IOCTL)
+               v4l_printk_ioctl(cmd);
+
+       switch (cmd) {
+       /* Query capabilities */
+       case VIDIOC_QUERYCAP:
+       {
+               struct v4l2_capability *cap = arg;
+
+               memset(cap, 0, sizeof *cap);
+               strncpy(cap->driver, "uvcvideo", sizeof cap->driver);
+               strncpy(cap->card, vdev->name, 32);
+               strncpy(cap->bus_info, video->dev->udev->bus->bus_name,
+                       sizeof cap->bus_info);
+               cap->version = DRIVER_VERSION_NUMBER;
+               cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
+                                 | V4L2_CAP_STREAMING;
+               break;
+       }
+
+       /* Get, Set & Query control */
+       case VIDIOC_QUERYCTRL:
+               return uvc_query_v4l2_ctrl(video, arg);
+
+       case VIDIOC_G_CTRL:
+       {
+               struct v4l2_control *ctrl = arg;
+               struct v4l2_ext_control xctrl;
+
+               memset(&xctrl, 0, sizeof xctrl);
+               xctrl.id = ctrl->id;
+
+               uvc_ctrl_begin(video);
+               ret = uvc_ctrl_get(video, &xctrl);
+               uvc_ctrl_rollback(video);
+               if (ret >= 0)
+                       ctrl->value = xctrl.value;
+               break;
+       }
+
+       case VIDIOC_S_CTRL:
+       {
+               struct v4l2_control *ctrl = arg;
+               struct v4l2_ext_control xctrl;
+
+               memset(&xctrl, 0, sizeof xctrl);
+               xctrl.id = ctrl->id;
+               xctrl.value = ctrl->value;
+
+               uvc_ctrl_begin(video);
+               ret = uvc_ctrl_set(video, &xctrl);
+               if (ret < 0) {
+                       uvc_ctrl_rollback(video);
+                       return ret;
+               }
+               ret = uvc_ctrl_commit(video);
+               break;
+       }
+
+       case VIDIOC_QUERYMENU:
+               return uvc_v4l2_query_menu(video, arg);
+
+       case VIDIOC_G_EXT_CTRLS:
+       {
+               struct v4l2_ext_controls *ctrls = arg;
+               struct v4l2_ext_control *ctrl = ctrls->controls;
+               unsigned int i;
+
+               uvc_ctrl_begin(video);
+               for (i = 0; i < ctrls->count; ++ctrl, ++i) {
+                       ret = uvc_ctrl_get(video, ctrl);
+                       if (ret < 0) {
+                               uvc_ctrl_rollback(video);
+                               ctrls->error_idx = i;
+                               return ret;
+                       }
+               }
+               ctrls->error_idx = 0;
+               ret = uvc_ctrl_rollback(video);
+               break;
+       }
+
+       case VIDIOC_S_EXT_CTRLS:
+       case VIDIOC_TRY_EXT_CTRLS:
+       {
+               struct v4l2_ext_controls *ctrls = arg;
+               struct v4l2_ext_control *ctrl = ctrls->controls;
+               unsigned int i;
+
+               ret = uvc_ctrl_begin(video);
+               if (ret < 0)
+                       return ret;
+
+               for (i = 0; i < ctrls->count; ++ctrl, ++i) {
+                       ret = uvc_ctrl_set(video, ctrl);
+                       if (ret < 0) {
+                               uvc_ctrl_rollback(video);
+                               ctrls->error_idx = i;
+                               return ret;
+                       }
+               }
+
+               ctrls->error_idx = 0;
+
+               if (cmd == VIDIOC_S_EXT_CTRLS)
+                       ret = uvc_ctrl_commit(video);
+               else
+                       ret = uvc_ctrl_rollback(video);
+               break;
+       }
+
+       /* Get, Set & Enum input */
+       case VIDIOC_ENUMINPUT:
+       {
+               const struct uvc_entity *selector = video->selector;
+               struct v4l2_input *input = arg;
+               struct uvc_entity *iterm = NULL;
+               u32 index = input->index;
+               int pin = 0;
+
+               if (selector == NULL ||
+                   (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+                       if (index != 0)
+                               return -EINVAL;
+                       iterm = list_first_entry(&video->iterms,
+                                       struct uvc_entity, chain);
+                       pin = iterm->id;
+               } else if (pin < selector->selector.bNrInPins) {
+                       pin = selector->selector.baSourceID[index];
+                       list_for_each_entry(iterm, video->iterms.next, chain) {
+                               if (iterm->id == pin)
+                                       break;
+                       }
+               }
+
+               if (iterm == NULL || iterm->id != pin)
+                       return -EINVAL;
+
+               memset(input, 0, sizeof *input);
+               input->index = index;
+               strncpy(input->name, iterm->name, sizeof input->name);
+               if (UVC_ENTITY_TYPE(iterm) == ITT_CAMERA)
+                       input->type = V4L2_INPUT_TYPE_CAMERA;
+               break;
+       }
+
+       case VIDIOC_G_INPUT:
+       {
+               u8 input;
+
+               if (video->selector == NULL ||
+                   (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+                       *(int *)arg = 0;
+                       break;
+               }
+
+               ret = uvc_query_ctrl(video->dev, GET_CUR, video->selector->id,
+                       video->dev->intfnum, SU_INPUT_SELECT_CONTROL,
+                       &input, 1);
+               if (ret < 0)
+                       return ret;
+
+               *(int *)arg = input - 1;
+               break;
+       }
+
+       case VIDIOC_S_INPUT:
+       {
+               u8 input = *(u32 *)arg + 1;
+
+               if ((ret = uvc_acquire_privileges(handle)) < 0)
+                       return ret;
+
+               if (video->selector == NULL ||
+                   (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
+                       if (input != 1)
+                               return -EINVAL;
+                       break;
+               }
+
+               if (input > video->selector->selector.bNrInPins)
+                       return -EINVAL;
+
+               return uvc_query_ctrl(video->dev, SET_CUR, video->selector->id,
+                       video->dev->intfnum, SU_INPUT_SELECT_CONTROL,
+                       &input, 1);
+       }
+
+       /* Try, Get, Set & Enum format */
+       case VIDIOC_ENUM_FMT:
+       {
+               struct v4l2_fmtdesc *fmt = arg;
+               struct uvc_format *format;
+
+               if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+                   fmt->index >= video->streaming->nformats)
+                       return -EINVAL;
+
+               format = &video->streaming->format[fmt->index];
+               fmt->flags = 0;
+               if (format->flags & UVC_FMT_FLAG_COMPRESSED)
+                       fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
+               strncpy(fmt->description, format->name,
+                       sizeof fmt->description);
+               fmt->description[sizeof fmt->description - 1] = 0;
+               fmt->pixelformat = format->fcc;
+               break;
+       }
+
+       case VIDIOC_TRY_FMT:
+       {
+               struct uvc_streaming_control probe;
+
+               if ((ret = uvc_acquire_privileges(handle)) < 0)
+                       return ret;
+
+               return uvc_v4l2_try_format(video, arg, &probe, NULL, NULL);
+       }
+
+       case VIDIOC_S_FMT:
+               if ((ret = uvc_acquire_privileges(handle)) < 0)
+                       return ret;
+
+               return uvc_v4l2_set_format(video, arg);
+
+       case VIDIOC_G_FMT:
+               return uvc_v4l2_get_format(video, arg);
+
+       /* Frame size enumeration */
+       case VIDIOC_ENUM_FRAMESIZES:
+       {
+               struct v4l2_frmsizeenum *fsize = arg;
+               struct uvc_format *format = NULL;
+               struct uvc_frame *frame;
+               int i;
+
+               /* Look for the given pixel format */
+               for (i = 0; i < video->streaming->nformats; i++) {
+                       if (video->streaming->format[i].fcc ==
+                                       fsize->pixel_format) {
+                               format = &video->streaming->format[i];
+                               break;
+                       }
+               }
+               if (format == NULL)
+                       return -EINVAL;
+
+               if (fsize->index >= format->nframes)
+                       return -EINVAL;
+
+               frame = &format->frame[fsize->index];
+               fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+               fsize->discrete.width = frame->wWidth;
+               fsize->discrete.height = frame->wHeight;
+               break;
+       }
+
+       /* Frame interval enumeration */
+       case VIDIOC_ENUM_FRAMEINTERVALS:
+       {
+               struct v4l2_frmivalenum *fival = arg;
+               struct uvc_format *format = NULL;
+               struct uvc_frame *frame = NULL;
+               int i;
+
+               /* Look for the given pixel format and frame size */
+               for (i = 0; i < video->streaming->nformats; i++) {
+                       if (video->streaming->format[i].fcc ==
+                                       fival->pixel_format) {
+                               format = &video->streaming->format[i];
+                               break;
+                       }
+               }
+               if (format == NULL)
+                       return -EINVAL;
+
+               for (i = 0; i < format->nframes; i++) {
+                       if (format->frame[i].wWidth == fival->width &&
+                           format->frame[i].wHeight == fival->height) {
+                               frame = &format->frame[i];
+                               break;
+                       }
+               }
+               if (frame == NULL)
+                       return -EINVAL;
+
+               if (frame->bFrameIntervalType) {
+                       if (fival->index >= frame->bFrameIntervalType)
+                               return -EINVAL;
+
+                       fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+                       fival->discrete.numerator =
+                               frame->dwFrameInterval[fival->index];
+                       fival->discrete.denominator = 10000000;
+                       uvc_simplify_fraction(&fival->discrete.numerator,
+                               &fival->discrete.denominator, 8, 333);
+               } else {
+                       fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
+                       fival->stepwise.min.numerator =
+                               frame->dwFrameInterval[0];
+                       fival->stepwise.min.denominator = 10000000;
+                       fival->stepwise.max.numerator =
+                               frame->dwFrameInterval[1];
+                       fival->stepwise.max.denominator = 10000000;
+                       fival->stepwise.step.numerator =
+                               frame->dwFrameInterval[2];
+                       fival->stepwise.step.denominator = 10000000;
+                       uvc_simplify_fraction(&fival->stepwise.min.numerator,
+                               &fival->stepwise.min.denominator, 8, 333);
+                       uvc_simplify_fraction(&fival->stepwise.max.numerator,
+                               &fival->stepwise.max.denominator, 8, 333);
+                       uvc_simplify_fraction(&fival->stepwise.step.numerator,
+                               &fival->stepwise.step.denominator, 8, 333);
+               }
+               break;
+       }
+
+       /* Get & Set streaming parameters */
+       case VIDIOC_G_PARM:
+               return uvc_v4l2_get_streamparm(video, arg);
+
+       case VIDIOC_S_PARM:
+               if ((ret = uvc_acquire_privileges(handle)) < 0)
+                       return ret;
+
+               return uvc_v4l2_set_streamparm(video, arg);
+
+       /* Cropping and scaling */
+       case VIDIOC_CROPCAP:
+       {
+               struct v4l2_cropcap *ccap = arg;
+               struct uvc_frame *frame = video->streaming->cur_frame;
+
+               if (ccap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+                       return -EINVAL;
+
+               ccap->bounds.left = 0;
+               ccap->bounds.top = 0;
+               ccap->bounds.width = frame->wWidth;
+               ccap->bounds.height = frame->wHeight;
+
+               ccap->defrect = ccap->bounds;
+
+               ccap->pixelaspect.numerator = 1;
+               ccap->pixelaspect.denominator = 1;
+               break;
+       }
+
+       case VIDIOC_G_CROP:
+       case VIDIOC_S_CROP:
+               return -EINVAL;
+
+       /* Buffers & streaming */
+       case VIDIOC_REQBUFS:
+       {
+               struct v4l2_requestbuffers *rb = arg;
+               unsigned int bufsize =
+                       video->streaming->ctrl.dwMaxVideoFrameSize;
+
+               if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+                   rb->memory != V4L2_MEMORY_MMAP)
+                       return -EINVAL;
+
+               if ((ret = uvc_acquire_privileges(handle)) < 0)
+                       return ret;
+
+               ret = uvc_alloc_buffers(&video->queue, rb->count, bufsize);
+               if (ret < 0)
+                       return ret;
+
+               if (!(video->streaming->cur_format->flags &
+                   UVC_FMT_FLAG_COMPRESSED))
+                       video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE;
+
+               rb->count = ret;
+               ret = 0;
+               break;
+       }
+
+       case VIDIOC_QUERYBUF:
+       {
+               struct v4l2_buffer *buf = arg;
+
+               if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+                       return -EINVAL;
+
+               if (!uvc_has_privileges(handle))
+                       return -EBUSY;
+
+               return uvc_query_buffer(&video->queue, buf);
+       }
+
+       case VIDIOC_QBUF:
+               if (!uvc_has_privileges(handle))
+                       return -EBUSY;
+
+               return uvc_queue_buffer(&video->queue, arg);
+
+       case VIDIOC_DQBUF:
+               if (!uvc_has_privileges(handle))
+                       return -EBUSY;
+
+               return uvc_dequeue_buffer(&video->queue, arg,
+                       file->f_flags & O_NONBLOCK);
+
+       case VIDIOC_STREAMON:
+       {
+               int *type = arg;
+
+               if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+                       return -EINVAL;
+
+               if (!uvc_has_privileges(handle))
+                       return -EBUSY;
+
+               if ((ret = uvc_video_enable(video, 1)) < 0)
+                       return ret;
+               break;
+       }
+
+       case VIDIOC_STREAMOFF:
+       {
+               int *type = arg;
+
+               if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+                       return -EINVAL;
+
+               if (!uvc_has_privileges(handle))
+                       return -EBUSY;
+
+               return uvc_video_enable(video, 0);
+       }
+
+       /* Analog video standards make no sense for digital cameras. */
+       case VIDIOC_ENUMSTD:
+       case VIDIOC_QUERYSTD:
+       case VIDIOC_G_STD:
+       case VIDIOC_S_STD:
+
+       case VIDIOC_OVERLAY:
+
+       case VIDIOC_ENUMAUDIO:
+       case VIDIOC_ENUMAUDOUT:
+
+       case VIDIOC_ENUMOUTPUT:
+               uvc_trace(UVC_TRACE_IOCTL, "Unsupported ioctl 0x%08x\n", cmd);
+               return -EINVAL;
+
+       /* Dynamic controls. */
+       case UVCIOC_CTRL_ADD:
+       {
+               struct uvc_xu_control_info *xinfo = arg;
+               struct uvc_control_info *info;
+
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+
+               info = kmalloc(sizeof *info, GFP_KERNEL);
+               if (info == NULL)
+                       return -ENOMEM;
+
+               memcpy(info->entity, xinfo->entity, sizeof info->entity);
+               info->index = xinfo->index;
+               info->selector = xinfo->selector;
+               info->size = xinfo->size;
+               info->flags = xinfo->flags;
+
+               info->flags |= UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX |
+                               UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF;
+
+               ret = uvc_ctrl_add_info(info);
+               if (ret < 0)
+                       kfree(info);
+               break;
+       }
+
+       case UVCIOC_CTRL_MAP:
+       {
+               struct uvc_xu_control_mapping *xmap = arg;
+               struct uvc_control_mapping *map;
+
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+
+               map = kmalloc(sizeof *map, GFP_KERNEL);
+               if (map == NULL)
+                       return -ENOMEM;
+
+               map->id = xmap->id;
+               memcpy(map->name, xmap->name, sizeof map->name);
+               memcpy(map->entity, xmap->entity, sizeof map->entity);
+               map->selector = xmap->selector;
+               map->size = xmap->size;
+               map->offset = xmap->offset;
+               map->v4l2_type = xmap->v4l2_type;
+               map->data_type = xmap->data_type;
+
+               ret = uvc_ctrl_add_mapping(map);
+               if (ret < 0)
+                       kfree(map);
+               break;
+       }
+
+       case UVCIOC_CTRL_GET:
+               return uvc_xu_ctrl_query(video, arg, 0);
+
+       case UVCIOC_CTRL_SET:
+               return uvc_xu_ctrl_query(video, arg, 1);
+
+       default:
+               if ((ret = v4l_compat_translate_ioctl(inode, file, cmd, arg,
+                       uvc_v4l2_do_ioctl)) == -ENOIOCTLCMD)
+                       uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n",
+                                 cmd);
+               return ret;
+       }
+
+       return ret;
+}
+
+static int uvc_v4l2_ioctl(struct inode *inode, struct file *file,
+                    unsigned int cmd, unsigned long arg)
+{
+       uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_ioctl\n");
+       return video_usercopy(inode, file, cmd, arg, uvc_v4l2_do_ioctl);
+}
+
+static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
+                   size_t count, loff_t *ppos)
+{
+       uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n");
+       return -ENODEV;
+}
+
+/*
+ * VMA operations.
+ */
+static void uvc_vm_open(struct vm_area_struct *vma)
+{
+       struct uvc_buffer *buffer = vma->vm_private_data;
+       buffer->vma_use_count++;
+}
+
+static void uvc_vm_close(struct vm_area_struct *vma)
+{
+       struct uvc_buffer *buffer = vma->vm_private_data;
+       buffer->vma_use_count--;
+}
+
+static struct vm_operations_struct uvc_vm_ops = {
+       .open           = uvc_vm_open,
+       .close          = uvc_vm_close,
+};
+
+static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct uvc_video_device *video = video_get_drvdata(vdev);
+       struct uvc_buffer *buffer;
+       struct page *page;
+       unsigned long addr, start, size;
+       unsigned int i;
+       int ret = 0;
+
+       uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_mmap\n");
+
+       start = vma->vm_start;
+       size = vma->vm_end - vma->vm_start;
+
+       mutex_lock(&video->queue.mutex);
+
+       for (i = 0; i < video->queue.count; ++i) {
+               buffer = &video->queue.buffer[i];
+               if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
+                       break;
+       }
+
+       if (i == video->queue.count || size != video->queue.buf_size) {
+               ret = -EINVAL;
+               goto done;
+       }
+
+       /*
+        * VM_IO marks the area as being an mmaped region for I/O to a
+        * device. It also prevents the region from being core dumped.
+        */
+       vma->vm_flags |= VM_IO;
+
+       addr = (unsigned long)video->queue.mem + buffer->buf.m.offset;
+       while (size > 0) {
+               page = vmalloc_to_page((void *)addr);
+               if ((ret = vm_insert_page(vma, start, page)) < 0)
+                       goto done;
+
+               start += PAGE_SIZE;
+               addr += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+
+       vma->vm_ops = &uvc_vm_ops;
+       vma->vm_private_data = buffer;
+       uvc_vm_open(vma);
+
+done:
+       mutex_unlock(&video->queue.mutex);
+       return ret;
+}
+
+static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait)
+{
+       struct video_device *vdev = video_devdata(file);
+       struct uvc_video_device *video = video_get_drvdata(vdev);
+
+       uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n");
+
+       return uvc_queue_poll(&video->queue, file, wait);
+}
+
+struct file_operations uvc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = uvc_v4l2_open,
+       .release        = uvc_v4l2_release,
+       .ioctl          = uvc_v4l2_ioctl,
+       .compat_ioctl   = v4l_compat_ioctl32,
+       .llseek         = no_llseek,
+       .read           = uvc_v4l2_read,
+       .mmap           = uvc_v4l2_mmap,
+       .poll           = uvc_v4l2_poll,
+};
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
new file mode 100644 (file)
index 0000000..6faf1fb
--- /dev/null
@@ -0,0 +1,934 @@
+/*
+ *      uvc_video.c  --  USB Video Class driver - Video handling
+ *
+ *      Copyright (C) 2005-2008
+ *          Laurent Pinchart (laurent.pinchart@skynet.be)
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+#include <asm/unaligned.h>
+
+#include <media/v4l2-common.h>
+
+#include "uvcvideo.h"
+
+/* ------------------------------------------------------------------------
+ * UVC Controls
+ */
+
+static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
+                       __u8 intfnum, __u8 cs, void *data, __u16 size,
+                       int timeout)
+{
+       __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+       unsigned int pipe;
+       int ret;
+
+       pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0)
+                             : usb_sndctrlpipe(dev->udev, 0);
+       type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
+
+       ret = usb_control_msg(dev->udev, pipe, query, type, cs << 8,
+                       unit << 8 | intfnum, data, size, timeout);
+
+       if (ret != size) {
+               uvc_printk(KERN_ERR, "Failed to query (%u) UVC control %u "
+                       "(unit %u) : %d (exp. %u).\n", query, cs, unit, ret,
+                       size);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
+                       __u8 intfnum, __u8 cs, void *data, __u16 size)
+{
+       return __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size,
+                               UVC_CTRL_CONTROL_TIMEOUT);
+}
+
+static void uvc_fixup_buffer_size(struct uvc_video_device *video,
+       struct uvc_streaming_control *ctrl)
+{
+       struct uvc_format *format;
+       struct uvc_frame *frame;
+
+       if (ctrl->bFormatIndex <= 0 ||
+           ctrl->bFormatIndex > video->streaming->nformats)
+               return;
+
+       format = &video->streaming->format[ctrl->bFormatIndex - 1];
+
+       if (ctrl->bFrameIndex <= 0 ||
+           ctrl->bFrameIndex > format->nframes)
+               return;
+
+       frame = &format->frame[ctrl->bFrameIndex - 1];
+
+       if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) ||
+            (ctrl->dwMaxVideoFrameSize == 0 &&
+             video->dev->uvc_version < 0x0110))
+               ctrl->dwMaxVideoFrameSize =
+                       frame->dwMaxVideoFrameBufferSize;
+}
+
+static int uvc_get_video_ctrl(struct uvc_video_device *video,
+       struct uvc_streaming_control *ctrl, int probe, __u8 query)
+{
+       __u8 data[34];
+       __u8 size;
+       int ret;
+
+       size = video->dev->uvc_version >= 0x0110 ? 34 : 26;
+       ret = __uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum,
+               probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, &data, size,
+               UVC_CTRL_STREAMING_TIMEOUT);
+
+       if (ret < 0)
+               return ret;
+
+       ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]);
+       ctrl->bFormatIndex = data[2];
+       ctrl->bFrameIndex = data[3];
+       ctrl->dwFrameInterval = le32_to_cpup((__le32 *)&data[4]);
+       ctrl->wKeyFrameRate = le16_to_cpup((__le16 *)&data[8]);
+       ctrl->wPFrameRate = le16_to_cpup((__le16 *)&data[10]);
+       ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]);
+       ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]);
+       ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]);
+       ctrl->dwMaxVideoFrameSize =
+               le32_to_cpu(get_unaligned((__le32 *)&data[18]));
+       ctrl->dwMaxPayloadTransferSize =
+               le32_to_cpu(get_unaligned((__le32 *)&data[22]));
+
+       if (size == 34) {
+               ctrl->dwClockFrequency =
+                       le32_to_cpu(get_unaligned((__le32 *)&data[26]));
+               ctrl->bmFramingInfo = data[30];
+               ctrl->bPreferedVersion = data[31];
+               ctrl->bMinVersion = data[32];
+               ctrl->bMaxVersion = data[33];
+       } else {
+               ctrl->dwClockFrequency = video->dev->clock_frequency;
+               ctrl->bmFramingInfo = 0;
+               ctrl->bPreferedVersion = 0;
+               ctrl->bMinVersion = 0;
+               ctrl->bMaxVersion = 0;
+       }
+
+       /* Some broken devices return a null or wrong dwMaxVideoFrameSize.
+        * Try to get the value from the format and frame descriptor.
+        */
+       uvc_fixup_buffer_size(video, ctrl);
+
+       return 0;
+}
+
+int uvc_set_video_ctrl(struct uvc_video_device *video,
+       struct uvc_streaming_control *ctrl, int probe)
+{
+       __u8 data[34];
+       __u8 size;
+
+       size = video->dev->uvc_version >= 0x0110 ? 34 : 26;
+       memset(data, 0, sizeof data);
+
+       *(__le16 *)&data[0] = cpu_to_le16(ctrl->bmHint);
+       data[2] = ctrl->bFormatIndex;
+       data[3] = ctrl->bFrameIndex;
+       *(__le32 *)&data[4] = cpu_to_le32(ctrl->dwFrameInterval);
+       *(__le16 *)&data[8] = cpu_to_le16(ctrl->wKeyFrameRate);
+       *(__le16 *)&data[10] = cpu_to_le16(ctrl->wPFrameRate);
+       *(__le16 *)&data[12] = cpu_to_le16(ctrl->wCompQuality);
+       *(__le16 *)&data[14] = cpu_to_le16(ctrl->wCompWindowSize);
+       *(__le16 *)&data[16] = cpu_to_le16(ctrl->wDelay);
+       /* Note: Some of the fields below are not required for IN devices (see
+        * UVC spec, 4.3.1.1), but we still copy them in case support for OUT
+        * devices is added in the future. */
+       put_unaligned(cpu_to_le32(ctrl->dwMaxVideoFrameSize),
+               (__le32 *)&data[18]);
+       put_unaligned(cpu_to_le32(ctrl->dwMaxPayloadTransferSize),
+               (__le32 *)&data[22]);
+
+       if (size == 34) {
+               put_unaligned(cpu_to_le32(ctrl->dwClockFrequency),
+                       (__le32 *)&data[26]);
+               data[30] = ctrl->bmFramingInfo;
+               data[31] = ctrl->bPreferedVersion;
+               data[32] = ctrl->bMinVersion;
+               data[33] = ctrl->bMaxVersion;
+       }
+
+       return __uvc_query_ctrl(video->dev, SET_CUR, 0,
+               video->streaming->intfnum,
+               probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, &data, size,
+               UVC_CTRL_STREAMING_TIMEOUT);
+}
+
+int uvc_probe_video(struct uvc_video_device *video,
+       struct uvc_streaming_control *probe)
+{
+       struct uvc_streaming_control probe_min, probe_max;
+       __u16 bandwidth;
+       unsigned int i;
+       int ret;
+
+       mutex_lock(&video->streaming->mutex);
+
+       /* Perform probing. The device should adjust the requested values
+        * according to its capabilities. However, some devices, namely the
+        * first generation UVC Logitech webcams, don't implement the Video
+        * Probe control properly, and just return the needed bandwidth. For
+        * that reason, if the needed bandwidth exceeds the maximum available
+        * bandwidth, try to lower the quality.
+        */
+       if ((ret = uvc_set_video_ctrl(video, probe, 1)) < 0)
+               goto done;
+
+       /* Get the minimum and maximum values for compression settings. */
+       if (!(video->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) {
+               ret = uvc_get_video_ctrl(video, &probe_min, 1, GET_MIN);
+               if (ret < 0)
+                       goto done;
+               ret = uvc_get_video_ctrl(video, &probe_max, 1, GET_MAX);
+               if (ret < 0)
+                       goto done;
+
+               probe->wCompQuality = probe_max.wCompQuality;
+       }
+
+       for (i = 0; i < 2; ++i) {
+               if ((ret = uvc_set_video_ctrl(video, probe, 1)) < 0 ||
+                   (ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0)
+                       goto done;
+
+               if (video->streaming->intf->num_altsetting == 1)
+                       break;
+
+               bandwidth = probe->dwMaxPayloadTransferSize;
+               if (bandwidth <= video->streaming->maxpsize)
+                       break;
+
+               if (video->dev->quirks & UVC_QUIRK_PROBE_MINMAX) {
+                       ret = -ENOSPC;
+                       goto done;
+               }
+
+               /* TODO: negotiate compression parameters */
+               probe->wKeyFrameRate = probe_min.wKeyFrameRate;
+               probe->wPFrameRate = probe_min.wPFrameRate;
+               probe->wCompQuality = probe_max.wCompQuality;
+               probe->wCompWindowSize = probe_min.wCompWindowSize;
+       }
+
+done:
+       mutex_unlock(&video->streaming->mutex);
+       return ret;
+}
+
+/* ------------------------------------------------------------------------
+ * Video codecs
+ */
+
+/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
+#define UVC_STREAM_EOH (1 << 7)
+#define UVC_STREAM_ERR (1 << 6)
+#define UVC_STREAM_STI (1 << 5)
+#define UVC_STREAM_RES (1 << 4)
+#define UVC_STREAM_SCR (1 << 3)
+#define UVC_STREAM_PTS (1 << 2)
+#define UVC_STREAM_EOF (1 << 1)
+#define UVC_STREAM_FID (1 << 0)
+
+/* Video payload decoding is handled by uvc_video_decode_start(),
+ * uvc_video_decode_data() and uvc_video_decode_end().
+ *
+ * uvc_video_decode_start is called with URB data at the start of a bulk or
+ * isochronous payload. It processes header data and returns the header size
+ * in bytes if successful. If an error occurs, it returns a negative error
+ * code. The following error codes have special meanings.
+ *
+ * - EAGAIN informs the caller that the current video buffer should be marked
+ *   as done, and that the function should be called again with the same data
+ *   and a new video buffer. This is used when end of frame conditions can be
+ *   reliably detected at the beginning of the next frame only.
+ *
+ * If an error other than -EAGAIN is returned, the caller will drop the current
+ * payload. No call to uvc_video_decode_data and uvc_video_decode_end will be
+ * made until the next payload. -ENODATA can be used to drop the current
+ * payload if no other error code is appropriate.
+ *
+ * uvc_video_decode_data is called for every URB with URB data. It copies the
+ * data to the video buffer.
+ *
+ * uvc_video_decode_end is called with header data at the end of a bulk or
+ * isochronous payload. It performs any additional header data processing and
+ * returns 0 or a negative error code if an error occured. As header data have
+ * already been processed by uvc_video_decode_start, this functions isn't
+ * required to perform sanity checks a second time.
+ *
+ * For isochronous transfers where a payload is always transfered in a single
+ * URB, the three functions will be called in a row.
+ *
+ * To let the decoder process header data and update its internal state even
+ * when no video buffer is available, uvc_video_decode_start must be prepared
+ * to be called with a NULL buf parameter. uvc_video_decode_data and
+ * uvc_video_decode_end will never be called with a NULL buffer.
+ */
+static int uvc_video_decode_start(struct uvc_video_device *video,
+               struct uvc_buffer *buf, const __u8 *data, int len)
+{
+       __u8 fid;
+
+       /* Sanity checks:
+        * - packet must be at least 2 bytes long
+        * - bHeaderLength value must be at least 2 bytes (see above)
+        * - bHeaderLength value can't be larger than the packet size.
+        */
+       if (len < 2 || data[0] < 2 || data[0] > len)
+               return -EINVAL;
+
+       /* Skip payloads marked with the error bit ("error frames"). */
+       if (data[1] & UVC_STREAM_ERR) {
+               uvc_trace(UVC_TRACE_FRAME, "Dropping payload (error bit "
+                         "set).\n");
+               return -ENODATA;
+       }
+
+       fid = data[1] & UVC_STREAM_FID;
+
+       /* Store the payload FID bit and return immediately when the buffer is
+        * NULL.
+        */
+       if (buf == NULL) {
+               video->last_fid = fid;
+               return -ENODATA;
+       }
+
+       /* Synchronize to the input stream by waiting for the FID bit to be
+        * toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE.
+        * queue->last_fid is initialized to -1, so the first isochronous
+        * frame will always be in sync.
+        *
+        * If the device doesn't toggle the FID bit, invert video->last_fid
+        * when the EOF bit is set to force synchronisation on the next packet.
+        */
+       if (buf->state != UVC_BUF_STATE_ACTIVE) {
+               if (fid == video->last_fid) {
+                       uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of "
+                               "sync).\n");
+                       if ((video->dev->quirks & UVC_QUIRK_STREAM_NO_FID) &&
+                           (data[1] & UVC_STREAM_EOF))
+                               video->last_fid ^= UVC_STREAM_FID;
+                       return -ENODATA;
+               }
+
+               /* TODO: Handle PTS and SCR. */
+               buf->state = UVC_BUF_STATE_ACTIVE;
+       }
+
+       /* Mark the buffer as done if we're at the beginning of a new frame.
+        * End of frame detection is better implemented by checking the EOF
+        * bit (FID bit toggling is delayed by one frame compared to the EOF
+        * bit), but some devices don't set the bit at end of frame (and the
+        * last payload can be lost anyway). We thus must check if the FID has
+        * been toggled.
+        *
+        * queue->last_fid is initialized to -1, so the first isochronous
+        * frame will never trigger an end of frame detection.
+        *
+        * Empty buffers (bytesused == 0) don't trigger end of frame detection
+        * as it doesn't make sense to return an empty buffer. This also
+        * avoids detecting and of frame conditions at FID toggling if the
+        * previous payload had the EOF bit set.
+        */
+       if (fid != video->last_fid && buf->buf.bytesused != 0) {
+               uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit "
+                               "toggled).\n");
+               buf->state = UVC_BUF_STATE_DONE;
+               return -EAGAIN;
+       }
+
+       video->last_fid = fid;
+
+       return data[0];
+}
+
+static void uvc_video_decode_data(struct uvc_video_device *video,
+               struct uvc_buffer *buf, const __u8 *data, int len)
+{
+       struct uvc_video_queue *queue = &video->queue;
+       unsigned int maxlen, nbytes;
+       void *mem;
+
+       if (len <= 0)
+               return;
+
+       /* Copy the video data to the buffer. */
+       maxlen = buf->buf.length - buf->buf.bytesused;
+       mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused;
+       nbytes = min((unsigned int)len, maxlen);
+       memcpy(mem, data, nbytes);
+       buf->buf.bytesused += nbytes;
+
+       /* Complete the current frame if the buffer size was exceeded. */
+       if (len > maxlen) {
+               uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n");
+               buf->state = UVC_BUF_STATE_DONE;
+       }
+}
+
+static void uvc_video_decode_end(struct uvc_video_device *video,
+               struct uvc_buffer *buf, const __u8 *data, int len)
+{
+       /* Mark the buffer as done if the EOF marker is set. */
+       if (data[1] & UVC_STREAM_EOF && buf->buf.bytesused != 0) {
+               uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n");
+               if (data[0] == len)
+                       uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n");
+               buf->state = UVC_BUF_STATE_DONE;
+               if (video->dev->quirks & UVC_QUIRK_STREAM_NO_FID)
+                       video->last_fid ^= UVC_STREAM_FID;
+       }
+}
+
+/* ------------------------------------------------------------------------
+ * URB handling
+ */
+
+/*
+ * Completion handler for video URBs.
+ */
+static void uvc_video_decode_isoc(struct urb *urb,
+       struct uvc_video_device *video, struct uvc_buffer *buf)
+{
+       u8 *mem;
+       int ret, i;
+
+       for (i = 0; i < urb->number_of_packets; ++i) {
+               if (urb->iso_frame_desc[i].status < 0) {
+                       uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame "
+                               "lost (%d).\n", urb->iso_frame_desc[i].status);
+                       continue;
+               }
+
+               /* Decode the payload header. */
+               mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+               do {
+                       ret = uvc_video_decode_start(video, buf, mem,
+                               urb->iso_frame_desc[i].actual_length);
+                       if (ret == -EAGAIN)
+                               buf = uvc_queue_next_buffer(&video->queue, buf);
+               } while (ret == -EAGAIN);
+
+               if (ret < 0)
+                       continue;
+
+               /* Decode the payload data. */
+               uvc_video_decode_data(video, buf, mem + ret,
+                       urb->iso_frame_desc[i].actual_length - ret);
+
+               /* Process the header again. */
+               uvc_video_decode_end(video, buf, mem, ret);
+
+               if (buf->state == UVC_BUF_STATE_DONE ||
+                   buf->state == UVC_BUF_STATE_ERROR)
+                       buf = uvc_queue_next_buffer(&video->queue, buf);
+       }
+}
+
+static void uvc_video_decode_bulk(struct urb *urb,
+       struct uvc_video_device *video, struct uvc_buffer *buf)
+{
+       u8 *mem;
+       int len, ret;
+
+       mem = urb->transfer_buffer;
+       len = urb->actual_length;
+       video->bulk.payload_size += len;
+
+       /* If the URB is the first of its payload, decode and save the
+        * header.
+        */
+       if (video->bulk.header_size == 0) {
+               do {
+                       ret = uvc_video_decode_start(video, buf, mem, len);
+                       if (ret == -EAGAIN)
+                               buf = uvc_queue_next_buffer(&video->queue, buf);
+               } while (ret == -EAGAIN);
+
+               /* If an error occured skip the rest of the payload. */
+               if (ret < 0 || buf == NULL) {
+                       video->bulk.skip_payload = 1;
+                       return;
+               }
+
+               video->bulk.header_size = ret;
+               memcpy(video->bulk.header, mem, video->bulk.header_size);
+
+               mem += ret;
+               len -= ret;
+       }
+
+       /* The buffer queue might have been cancelled while a bulk transfer
+        * was in progress, so we can reach here with buf equal to NULL. Make
+        * sure buf is never dereferenced if NULL.
+        */
+
+       /* Process video data. */
+       if (!video->bulk.skip_payload && buf != NULL)
+               uvc_video_decode_data(video, buf, mem, len);
+
+       /* Detect the payload end by a URB smaller than the maximum size (or
+        * a payload size equal to the maximum) and process the header again.
+        */
+       if (urb->actual_length < urb->transfer_buffer_length ||
+           video->bulk.payload_size >= video->bulk.max_payload_size) {
+               if (!video->bulk.skip_payload && buf != NULL) {
+                       uvc_video_decode_end(video, buf, video->bulk.header,
+                               video->bulk.header_size);
+                       if (buf->state == UVC_BUF_STATE_DONE ||
+                           buf->state == UVC_BUF_STATE_ERROR)
+                               buf = uvc_queue_next_buffer(&video->queue, buf);
+               }
+
+               video->bulk.header_size = 0;
+               video->bulk.skip_payload = 0;
+               video->bulk.payload_size = 0;
+       }
+}
+
+static void uvc_video_complete(struct urb *urb)
+{
+       struct uvc_video_device *video = urb->context;
+       struct uvc_video_queue *queue = &video->queue;
+       struct uvc_buffer *buf = NULL;
+       unsigned long flags;
+       int ret;
+
+       switch (urb->status) {
+       case 0:
+               break;
+
+       default:
+               uvc_printk(KERN_WARNING, "Non-zero status (%d) in video "
+                       "completion handler.\n", urb->status);
+
+       case -ENOENT:           /* usb_kill_urb() called. */
+               if (video->frozen)
+                       return;
+
+       case -ECONNRESET:       /* usb_unlink_urb() called. */
+       case -ESHUTDOWN:        /* The endpoint is being disabled. */
+               uvc_queue_cancel(queue, urb->status == -ESHUTDOWN);
+               return;
+       }
+
+       spin_lock_irqsave(&queue->irqlock, flags);
+       if (!list_empty(&queue->irqqueue))
+               buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
+                                      queue);
+       spin_unlock_irqrestore(&queue->irqlock, flags);
+
+       video->decode(urb, video, buf);
+
+       if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+               uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n",
+                       ret);
+       }
+}
+
+/*
+ * Uninitialize isochronous/bulk URBs and free transfer buffers.
+ */
+static void uvc_uninit_video(struct uvc_video_device *video)
+{
+       struct urb *urb;
+       unsigned int i;
+
+       for (i = 0; i < UVC_URBS; ++i) {
+               if ((urb = video->urb[i]) == NULL)
+                       continue;
+
+               usb_kill_urb(urb);
+               /* urb->transfer_buffer_length is not touched by USB core, so
+                * we can use it here as the buffer length.
+                */
+               if (video->urb_buffer[i]) {
+                       usb_buffer_free(video->dev->udev,
+                               urb->transfer_buffer_length,
+                               video->urb_buffer[i], urb->transfer_dma);
+                       video->urb_buffer[i] = NULL;
+               }
+
+               usb_free_urb(urb);
+               video->urb[i] = NULL;
+       }
+}
+
+/*
+ * Initialize isochronous URBs and allocate transfer buffers. The packet size
+ * is given by the endpoint.
+ */
+static int uvc_init_video_isoc(struct uvc_video_device *video,
+       struct usb_host_endpoint *ep)
+{
+       struct urb *urb;
+       unsigned int npackets, i, j;
+       __u16 psize;
+       __u32 size;
+
+       /* Compute the number of isochronous packets to allocate by dividing
+        * the maximum video frame size by the packet size. Limit the result
+        * to UVC_MAX_ISO_PACKETS.
+        */
+       psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+       psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+
+       size = video->streaming->ctrl.dwMaxVideoFrameSize;
+       if (size > UVC_MAX_FRAME_SIZE)
+               return -EINVAL;
+
+       npackets = (size + psize - 1) / psize;
+       if (npackets > UVC_MAX_ISO_PACKETS)
+               npackets = UVC_MAX_ISO_PACKETS;
+
+       size = npackets * psize;
+
+       for (i = 0; i < UVC_URBS; ++i) {
+               urb = usb_alloc_urb(npackets, GFP_KERNEL);
+               if (urb == NULL) {
+                       uvc_uninit_video(video);
+                       return -ENOMEM;
+               }
+
+               video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev,
+                       size, GFP_KERNEL, &urb->transfer_dma);
+               if (video->urb_buffer[i] == NULL) {
+                       usb_free_urb(urb);
+                       uvc_uninit_video(video);
+                       return -ENOMEM;
+               }
+
+               urb->dev = video->dev->udev;
+               urb->context = video;
+               urb->pipe = usb_rcvisocpipe(video->dev->udev,
+                               ep->desc.bEndpointAddress);
+               urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+               urb->interval = ep->desc.bInterval;
+               urb->transfer_buffer = video->urb_buffer[i];
+               urb->complete = uvc_video_complete;
+               urb->number_of_packets = npackets;
+               urb->transfer_buffer_length = size;
+
+               for (j = 0; j < npackets; ++j) {
+                       urb->iso_frame_desc[j].offset = j * psize;
+                       urb->iso_frame_desc[j].length = psize;
+               }
+
+               video->urb[i] = urb;
+       }
+
+       return 0;
+}
+
+/*
+ * Initialize bulk URBs and allocate transfer buffers. The packet size is
+ * given by the endpoint.
+ */
+static int uvc_init_video_bulk(struct uvc_video_device *video,
+       struct usb_host_endpoint *ep)
+{
+       struct urb *urb;
+       unsigned int pipe, i;
+       __u16 psize;
+       __u32 size;
+
+       /* Compute the bulk URB size. Some devices set the maximum payload
+        * size to a value too high for memory-constrained devices. We must
+        * then transfer the payload accross multiple URBs. To be consistant
+        * with isochronous mode, allocate maximum UVC_MAX_ISO_PACKETS per bulk
+        * URB.
+        */
+       psize = le16_to_cpu(ep->desc.wMaxPacketSize) & 0x07ff;
+       size = video->streaming->ctrl.dwMaxPayloadTransferSize;
+       video->bulk.max_payload_size = size;
+       if (size > psize * UVC_MAX_ISO_PACKETS)
+               size = psize * UVC_MAX_ISO_PACKETS;
+
+       pipe = usb_rcvbulkpipe(video->dev->udev, ep->desc.bEndpointAddress);
+
+       for (i = 0; i < UVC_URBS; ++i) {
+               urb = usb_alloc_urb(0, GFP_KERNEL);
+               if (urb == NULL) {
+                       uvc_uninit_video(video);
+                       return -ENOMEM;
+               }
+
+               video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev,
+                       size, GFP_KERNEL, &urb->transfer_dma);
+               if (video->urb_buffer[i] == NULL) {
+                       usb_free_urb(urb);
+                       uvc_uninit_video(video);
+                       return -ENOMEM;
+               }
+
+               usb_fill_bulk_urb(urb, video->dev->udev, pipe,
+                       video->urb_buffer[i], size, uvc_video_complete,
+                       video);
+               urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
+
+               video->urb[i] = urb;
+       }
+
+       return 0;
+}
+
+/*
+ * Initialize isochronous/bulk URBs and allocate transfer buffers.
+ */
+static int uvc_init_video(struct uvc_video_device *video)
+{
+       struct usb_interface *intf = video->streaming->intf;
+       struct usb_host_interface *alts;
+       struct usb_host_endpoint *ep = NULL;
+       int intfnum = video->streaming->intfnum;
+       unsigned int bandwidth, psize, i;
+       int ret;
+
+       video->last_fid = -1;
+       video->bulk.header_size = 0;
+       video->bulk.skip_payload = 0;
+       video->bulk.payload_size = 0;
+
+       if (intf->num_altsetting > 1) {
+               /* Isochronous endpoint, select the alternate setting. */
+               bandwidth = video->streaming->ctrl.dwMaxPayloadTransferSize;
+
+               if (bandwidth == 0) {
+                       uvc_printk(KERN_WARNING, "device %s requested null "
+                               "bandwidth, defaulting to lowest.\n",
+                               video->vdev->name);
+                       bandwidth = 1;
+               }
+
+               for (i = 0; i < intf->num_altsetting; ++i) {
+                       alts = &intf->altsetting[i];
+                       ep = uvc_find_endpoint(alts,
+                               video->streaming->header.bEndpointAddress);
+                       if (ep == NULL)
+                               continue;
+
+                       /* Check if the bandwidth is high enough. */
+                       psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+                       psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+                       if (psize >= bandwidth)
+                               break;
+               }
+
+               if (i >= intf->num_altsetting)
+                       return -EIO;
+
+               if ((ret = usb_set_interface(video->dev->udev, intfnum, i)) < 0)
+                       return ret;
+
+               ret = uvc_init_video_isoc(video, ep);
+       } else {
+               /* Bulk endpoint, proceed to URB initialization. */
+               ep = uvc_find_endpoint(&intf->altsetting[0],
+                               video->streaming->header.bEndpointAddress);
+               if (ep == NULL)
+                       return -EIO;
+
+               ret = uvc_init_video_bulk(video, ep);
+       }
+
+       if (ret < 0)
+               return ret;
+
+       /* Submit the URBs. */
+       for (i = 0; i < UVC_URBS; ++i) {
+               if ((ret = usb_submit_urb(video->urb[i], GFP_KERNEL)) < 0) {
+                       uvc_printk(KERN_ERR, "Failed to submit URB %u "
+                                       "(%d).\n", i, ret);
+                       uvc_uninit_video(video);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * Suspend/resume
+ */
+
+/*
+ * Stop streaming without disabling the video queue.
+ *
+ * To let userspace applications resume without trouble, we must not touch the
+ * video buffers in any way. We mark the device as frozen to make sure the URB
+ * completion handler won't try to cancel the queue when we kill the URBs.
+ */
+int uvc_video_suspend(struct uvc_video_device *video)
+{
+       if (!uvc_queue_streaming(&video->queue))
+               return 0;
+
+       video->frozen = 1;
+       uvc_uninit_video(video);
+       usb_set_interface(video->dev->udev, video->streaming->intfnum, 0);
+       return 0;
+}
+
+/*
+ * Reconfigure the video interface and restart streaming if it was enable
+ * before suspend.
+ *
+ * If an error occurs, disable the video queue. This will wake all pending
+ * buffers, making sure userspace applications are notified of the problem
+ * instead of waiting forever.
+ */
+int uvc_video_resume(struct uvc_video_device *video)
+{
+       int ret;
+
+       video->frozen = 0;
+
+       if ((ret = uvc_set_video_ctrl(video, &video->streaming->ctrl, 0)) < 0) {
+               uvc_queue_enable(&video->queue, 0);
+               return ret;
+       }
+
+       if (!uvc_queue_streaming(&video->queue))
+               return 0;
+
+       if ((ret = uvc_init_video(video)) < 0)
+               uvc_queue_enable(&video->queue, 0);
+
+       return ret;
+}
+
+/* ------------------------------------------------------------------------
+ * Video device
+ */
+
+/*
+ * Initialize the UVC video device by retrieving the default format and
+ * committing it.
+ *
+ * Some cameras (namely the Fuji Finepix) set the format and frame
+ * indexes to zero. The UVC standard doesn't clearly make this a spec
+ * violation, so try to silently fix the values if possible.
+ *
+ * This function is called before registering the device with V4L.
+ */
+int uvc_video_init(struct uvc_video_device *video)
+{
+       struct uvc_streaming_control *probe = &video->streaming->ctrl;
+       struct uvc_format *format = NULL;
+       struct uvc_frame *frame = NULL;
+       unsigned int i;
+       int ret;
+
+       if (video->streaming->nformats == 0) {
+               uvc_printk(KERN_INFO, "No supported video formats found.\n");
+               return -EINVAL;
+       }
+
+       /* Alternate setting 0 should be the default, yet the XBox Live Vision
+        * Cam (and possibly other devices) crash or otherwise misbehave if
+        * they don't receive a SET_INTERFACE request before any other video
+        * control request.
+        */
+       usb_set_interface(video->dev->udev, video->streaming->intfnum, 0);
+
+       /* Some webcams don't suport GET_DEF request on the probe control. We
+        * fall back to GET_CUR if GET_DEF fails.
+        */
+       if ((ret = uvc_get_video_ctrl(video, probe, 1, GET_DEF)) < 0 &&
+           (ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0)
+               return ret;
+
+       /* Check if the default format descriptor exists. Use the first
+        * available format otherwise.
+        */
+       for (i = video->streaming->nformats; i > 0; --i) {
+               format = &video->streaming->format[i-1];
+               if (format->index == probe->bFormatIndex)
+                       break;
+       }
+
+       if (format->nframes == 0) {
+               uvc_printk(KERN_INFO, "No frame descriptor found for the "
+                       "default format.\n");
+               return -EINVAL;
+       }
+
+       /* Zero bFrameIndex might be correct. Stream-based formats (including
+        * MPEG-2 TS and DV) do not support frames but have a dummy frame
+        * descriptor with bFrameIndex set to zero. If the default frame
+        * descriptor is not found, use the first avalable frame.
+        */
+       for (i = format->nframes; i > 0; --i) {
+               frame = &format->frame[i-1];
+               if (frame->bFrameIndex == probe->bFrameIndex)
+                       break;
+       }
+
+       /* Commit the default settings. */
+       probe->bFormatIndex = format->index;
+       probe->bFrameIndex = frame->bFrameIndex;
+       if ((ret = uvc_set_video_ctrl(video, probe, 0)) < 0)
+               return ret;
+
+       video->streaming->cur_format = format;
+       video->streaming->cur_frame = frame;
+       atomic_set(&video->active, 0);
+
+       /* Select the video decoding function */
+       if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
+               video->decode = uvc_video_decode_isight;
+       else if (video->streaming->intf->num_altsetting > 1)
+               video->decode = uvc_video_decode_isoc;
+       else
+               video->decode = uvc_video_decode_bulk;
+
+       return 0;
+}
+
+/*
+ * Enable or disable the video stream.
+ */
+int uvc_video_enable(struct uvc_video_device *video, int enable)
+{
+       int ret;
+
+       if (!enable) {
+               uvc_uninit_video(video);
+               usb_set_interface(video->dev->udev,
+                       video->streaming->intfnum, 0);
+               uvc_queue_enable(&video->queue, 0);
+               return 0;
+       }
+
+       if ((ret = uvc_queue_enable(&video->queue, 1)) < 0)
+               return ret;
+
+       return uvc_init_video(video);
+}
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
new file mode 100644 (file)
index 0000000..a995a78
--- /dev/null
@@ -0,0 +1,796 @@
+#ifndef _USB_VIDEO_H_
+#define _USB_VIDEO_H_
+
+#include <linux/kernel.h>
+#include <linux/videodev2.h>
+
+
+/*
+ * Dynamic controls
+ */
+
+/* Data types for UVC control data */
+#define UVC_CTRL_DATA_TYPE_RAW         0
+#define UVC_CTRL_DATA_TYPE_SIGNED      1
+#define UVC_CTRL_DATA_TYPE_UNSIGNED    2
+#define UVC_CTRL_DATA_TYPE_BOOLEAN     3
+#define UVC_CTRL_DATA_TYPE_ENUM                4
+#define UVC_CTRL_DATA_TYPE_BITMASK     5
+
+/* Control flags */
+#define UVC_CONTROL_SET_CUR    (1 << 0)
+#define UVC_CONTROL_GET_CUR    (1 << 1)
+#define UVC_CONTROL_GET_MIN    (1 << 2)
+#define UVC_CONTROL_GET_MAX    (1 << 3)
+#define UVC_CONTROL_GET_RES    (1 << 4)
+#define UVC_CONTROL_GET_DEF    (1 << 5)
+/* Control should be saved at suspend and restored at resume. */
+#define UVC_CONTROL_RESTORE    (1 << 6)
+/* Control can be updated by the camera. */
+#define UVC_CONTROL_AUTO_UPDATE        (1 << 7)
+
+#define UVC_CONTROL_GET_RANGE  (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \
+                                UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \
+                                UVC_CONTROL_GET_DEF)
+
+struct uvc_xu_control_info {
+       __u8 entity[16];
+       __u8 index;
+       __u8 selector;
+       __u16 size;
+       __u32 flags;
+};
+
+struct uvc_xu_control_mapping {
+       __u32 id;
+       __u8 name[32];
+       __u8 entity[16];
+       __u8 selector;
+
+       __u8 size;
+       __u8 offset;
+       enum v4l2_ctrl_type v4l2_type;
+       __u32 data_type;
+};
+
+struct uvc_xu_control {
+       __u8 unit;
+       __u8 selector;
+       __u16 size;
+       __u8 __user *data;
+};
+
+#define UVCIOC_CTRL_ADD                _IOW('U', 1, struct uvc_xu_control_info)
+#define UVCIOC_CTRL_MAP                _IOWR('U', 2, struct uvc_xu_control_mapping)
+#define UVCIOC_CTRL_GET                _IOWR('U', 3, struct uvc_xu_control)
+#define UVCIOC_CTRL_SET                _IOW('U', 4, struct uvc_xu_control)
+
+#ifdef __KERNEL__
+
+#include <linux/poll.h>
+
+/* --------------------------------------------------------------------------
+ * UVC constants
+ */
+
+#define SC_UNDEFINED                    0x00
+#define SC_VIDEOCONTROL                 0x01
+#define SC_VIDEOSTREAMING               0x02
+#define SC_VIDEO_INTERFACE_COLLECTION   0x03
+
+#define PC_PROTOCOL_UNDEFINED           0x00
+
+#define CS_UNDEFINED                    0x20
+#define CS_DEVICE                       0x21
+#define CS_CONFIGURATION                0x22
+#define CS_STRING                       0x23
+#define CS_INTERFACE                    0x24
+#define CS_ENDPOINT                     0x25
+
+/* VideoControl class specific interface descriptor */
+#define VC_DESCRIPTOR_UNDEFINED         0x00
+#define VC_HEADER                       0x01
+#define VC_INPUT_TERMINAL               0x02
+#define VC_OUTPUT_TERMINAL              0x03
+#define VC_SELECTOR_UNIT                0x04
+#define VC_PROCESSING_UNIT              0x05
+#define VC_EXTENSION_UNIT               0x06
+
+/* VideoStreaming class specific interface descriptor */
+#define VS_UNDEFINED                    0x00
+#define VS_INPUT_HEADER                 0x01
+#define VS_OUTPUT_HEADER                0x02
+#define VS_STILL_IMAGE_FRAME            0x03
+#define VS_FORMAT_UNCOMPRESSED          0x04
+#define VS_FRAME_UNCOMPRESSED           0x05
+#define VS_FORMAT_MJPEG                 0x06
+#define VS_FRAME_MJPEG                  0x07
+#define VS_FORMAT_MPEG2TS               0x0a
+#define VS_FORMAT_DV                    0x0c
+#define VS_COLORFORMAT                  0x0d
+#define VS_FORMAT_FRAME_BASED           0x10
+#define VS_FRAME_FRAME_BASED            0x11
+#define VS_FORMAT_STREAM_BASED          0x12
+
+/* Endpoint type */
+#define EP_UNDEFINED                    0x00
+#define EP_GENERAL                      0x01
+#define EP_ENDPOINT                     0x02
+#define EP_INTERRUPT                    0x03
+
+/* Request codes */
+#define RC_UNDEFINED                    0x00
+#define SET_CUR                         0x01
+#define GET_CUR                         0x81
+#define GET_MIN                         0x82
+#define GET_MAX                         0x83
+#define GET_RES                         0x84
+#define GET_LEN                         0x85
+#define GET_INFO                        0x86
+#define GET_DEF                         0x87
+
+/* VideoControl interface controls */
+#define VC_CONTROL_UNDEFINED            0x00
+#define VC_VIDEO_POWER_MODE_CONTROL     0x01
+#define VC_REQUEST_ERROR_CODE_CONTROL   0x02
+
+/* Terminal controls */
+#define TE_CONTROL_UNDEFINED            0x00
+
+/* Selector Unit controls */
+#define SU_CONTROL_UNDEFINED            0x00
+#define SU_INPUT_SELECT_CONTROL         0x01
+
+/* Camera Terminal controls */
+#define CT_CONTROL_UNDEFINED                           0x00
+#define CT_SCANNING_MODE_CONTROL                       0x01
+#define CT_AE_MODE_CONTROL                             0x02
+#define CT_AE_PRIORITY_CONTROL                         0x03
+#define CT_EXPOSURE_TIME_ABSOLUTE_CONTROL               0x04
+#define CT_EXPOSURE_TIME_RELATIVE_CONTROL               0x05
+#define CT_FOCUS_ABSOLUTE_CONTROL                      0x06
+#define CT_FOCUS_RELATIVE_CONTROL                      0x07
+#define CT_FOCUS_AUTO_CONTROL                          0x08
+#define CT_IRIS_ABSOLUTE_CONTROL                       0x09
+#define CT_IRIS_RELATIVE_CONTROL                       0x0a
+#define CT_ZOOM_ABSOLUTE_CONTROL                       0x0b
+#define CT_ZOOM_RELATIVE_CONTROL                       0x0c
+#define CT_PANTILT_ABSOLUTE_CONTROL                    0x0d
+#define CT_PANTILT_RELATIVE_CONTROL                    0x0e
+#define CT_ROLL_ABSOLUTE_CONTROL                       0x0f
+#define CT_ROLL_RELATIVE_CONTROL                       0x10
+#define CT_PRIVACY_CONTROL                             0x11
+
+/* Processing Unit controls */
+#define PU_CONTROL_UNDEFINED                           0x00
+#define PU_BACKLIGHT_COMPENSATION_CONTROL               0x01
+#define PU_BRIGHTNESS_CONTROL                          0x02
+#define PU_CONTRAST_CONTROL                            0x03
+#define PU_GAIN_CONTROL                                0x04
+#define PU_POWER_LINE_FREQUENCY_CONTROL                0x05
+#define PU_HUE_CONTROL                                 0x06
+#define PU_SATURATION_CONTROL                          0x07
+#define PU_SHARPNESS_CONTROL                           0x08
+#define PU_GAMMA_CONTROL                               0x09
+#define PU_WHITE_BALANCE_TEMPERATURE_CONTROL            0x0a
+#define PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL       0x0b
+#define PU_WHITE_BALANCE_COMPONENT_CONTROL              0x0c
+#define PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL         0x0d
+#define PU_DIGITAL_MULTIPLIER_CONTROL                  0x0e
+#define PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL             0x0f
+#define PU_HUE_AUTO_CONTROL                            0x10
+#define PU_ANALOG_VIDEO_STANDARD_CONTROL                0x11
+#define PU_ANALOG_LOCK_STATUS_CONTROL                  0x12
+
+#define LXU_MOTOR_PANTILT_RELATIVE_CONTROL             0x01
+#define LXU_MOTOR_PANTILT_RESET_CONTROL                        0x02
+#define LXU_MOTOR_FOCUS_MOTOR_CONTROL                  0x03
+
+/* VideoStreaming interface controls */
+#define VS_CONTROL_UNDEFINED            0x00
+#define VS_PROBE_CONTROL                0x01
+#define VS_COMMIT_CONTROL               0x02
+#define VS_STILL_PROBE_CONTROL          0x03
+#define VS_STILL_COMMIT_CONTROL