/*
 *      uvcvideo.c  --  USB Video Class driver
 *
 *      Copyright (C) 2005-2006
 *          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.
 *
 */

/*
 * WARNING: This driver is definitely *NOT* complete. It will (hopefully)
 * support UVC devices with a camera sensors, a processing unit and several
 * optional extension units. Everything else is unsupported.
 *
 * 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.
 *
 * Multiple open is not supported. Opening the driver multiple times will fail
 * with -EBUSY. This might or might not be fixed soon.
 *
 * Although the driver compiles on 2.6.12, you should use a 2.6.15 or newer
 * kernel because of USB issues.
 */
 
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/videodev.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <asm/atomic.h>
#include <asm/semaphore.h>

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15)
#include <media/v4l2-common.h>
#endif

#include "uvcvideo.h"

#define DRIVER_AUTHOR		"Laurent Pinchart <laurent.pinchart@skynet.be>"
#define DRIVER_DESC		"USB Video Class driver"
#define DRIVER_VERSION		"0.1.0-d"
#define DRIVER_VERSION_NUMBER	KERNEL_VERSION(0, 1, 0)

#define UVC_CTRL_TIMEOUT	300


#define UVC_GUID_UVC_CAMERA	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
				 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}
#define UVC_GUID_UVC_PROCESSING	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
				 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}

#define UVC_GUID_LOGITECH_XU1	{0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
				 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1d}
#define UVC_GUID_LOGITECH_XU2	{0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
				 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1e}
#define UVC_GUID_LOGITECH_XU3	{0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
				 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f}
#define UVC_GUID_LOGITECH_MOTOR	{0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
				 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x56}

#define UVC_GUID_FORMAT_MJPEG	{0x4d, 0x4a, 0x50, 0x47, 0x00, 0x00, 0x10, 0x00, \
				 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
#define UVC_GUID_FORMAT_YUY2	{0x59, 0x55, 0x59, 0x32, 0x00, 0x00, 0x10, 0x00, \
				 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
#define UVC_GUID_FORMAT_YUY2A	{0x59, 0x55, 0x59, 0x32, 0x00, 0x00, 0x10, 0x00, \
				 0x00, 0x80, 0x71, 0x9b, 0x38, 0x00, 0xaa, 0x00}
#define UVC_GUID_FORMAT_UYVY    {0x55, 0x59, 0x56, 0x59, 0x00, 0x00, 0x10, 0x00, \
				 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
#define UVC_GUID_FORMAT_NV12	{0x4e, 0x56, 0x31, 0x32, 0x00, 0x00, 0x10, 0x00, \
				 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}

#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)

#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)

/* Number of isochronous URBs. */
#define UVC_URBS		5
/* Maximum number of packets per isochronous URB. */
#define UVC_MAX_ISO_PACKETS	40
/* Maximum frame size in bytes, for sanity checking. */
#define UVC_MAX_FRAME_SIZE	(16*1024*1024)
/* Maximum number of video buffers. */
#define UVC_MAX_VIDEO_BUFFERS	32

/* ------------------------------------------------------------------------
 * Structures
 */

struct uvc_device;

/* TODO: Put the most frequently accessed fields at the beginning of
 * structures to maximize cache efficiency.
 */
struct uvc_streaming_control {
	__u16 bmHint;
	__u8  bFormatIndex;
	__u8  bFrameIndex;
	__u32 dwFrameInterval;
	__u16 wKeyFrameRate;
	__u16 wPFrameRate;
	__u16 wCompQuality;
	__u16 wCompWindowSize;
	__u16 wDelay;
	__u32 dwMaxVideoFrameSize;
	__u32 dwMaxPayloadTransferSize;
	__u32 dwClockFrequency;
	__u8  bmFramingInfo;
	__u8  bPreferedVersion;
	__u8  bMinVersion;
	__u8  bMaxVersion;
};

struct uvc_menu_info {
	__u32 index;
	__u8 name[32];
};

struct uvc_control_desc {
	__u32 v4l2;
	enum v4l2_ctrl_type type;
	__u8 name[32];

	__u8 id;
	__u8 bit;
	__u8 size;
	__u8 unit[16];
	__u8 flags;
	
	struct uvc_menu_info *menu_info;
	__u32 menu_count;
};

struct uvc_format_desc {
	__u8 guid[16];
	__u32 fcc;
};

struct uvc_terminal {
	struct list_head list;

	__u8 id;
	__u16 type;
	char name[64];

	union {
		struct {
			__u16 wObjectiveFocalLengthMin;
			__u16 wObjectiveFocalLengthMax;
			__u16 wOcularFocalLength;
			__u8  bControlSize;
			__u8  *bmControls;
		} camera;

		struct {
			__u8  bSourceID;
		} output;
	};
};

struct uvc_unit {
	struct list_head list;

	int id;
	int type;
	char name[64];

	union {
		struct {
			__u8  bSourceID;
			__u16 wMaxMultiplier;
			__u8  bControlSize;
			__u8  *bmControls;
			__u8  bmVideoStandards;
		} processing;

		struct {
			__u8  bNrInPins;
			__u8  *baSourceID;
		} selector;

		struct {
		        __u8  guidExtensionCode[16];
		        __u8  bNumControls;
		        __u8  bNrInPins;
		        __u8  *baSourceID;
		        __u8  bControlSize;
		        __u8  *bmControls;
		        __u8  *bmControlsType;
		} extension;
	};
};

struct uvc_frame {
	__u8  bFrameIndex;
	__u8  bmCapabilities;
	__u16 wWidth;
	__u16 wHeight;
	__u32 dwMinBitRate;
	__u32 dwMaxBitRate;
	__u32 dwMaxVideoFrameBufferSize;
	__u8  bFrameIntervalType;
	__u32 dwDefaultFrameInterval;
	__u32 *dwFrameInterval;
};

struct uvc_format {
	__u8 type;
	__u8 index;
	__u8 bpp;
	__u8 colorspace;
	__u32 fcc;
	__u32 flags;

	char name[32];

	unsigned int nframes;
	struct uvc_frame *frame;
};

struct uvc_input_header {
	__u8 bNumFormats;
	__u8 bEndpointAddress;
	__u8 bmInfo;
	__u8 bTerminalLink;
	__u8 bStillCaptureMethod;
	__u8 bTriggerSupport;
	__u8 bTriggerUsage;
	__u8 bControlSize;
	__u8 *bmaControls;
};

struct uvc_output_header {
};

struct uvc_streaming {
	struct list_head list;

	struct usb_interface *intf;
	int intfnum;
	__u16 maxpsize;

	union {
		struct uvc_input_header input;
		struct uvc_output_header output;
	};

	unsigned int nformats;
	struct uvc_format *format;

	struct uvc_streaming_control ctrl;
	struct uvc_format *cur_format;
	struct uvc_frame *cur_frame;

	struct semaphore lock;
};

enum uvc_stream_state {
	UVC_STREAM_OFF = 0,
	UVC_STREAM_INTERRUPT = 1,
	UVC_STREAM_ON = 2,
};

enum uvc_buffer_state {
	UVC_BUF_STATE_IDLE       = 0,
	UVC_BUF_STATE_QUEUED     = 1,
	UVC_BUF_STATE_ACTIVE     = 2,
	UVC_BUF_STATE_DONE       = 3,
	UVC_BUF_STATE_ERROR      = 4,
};

struct uvc_buffer {
	unsigned int size;
	unsigned long vma_use_count;
	struct list_head stream;

	/* Touched by interrupt handler. */
	struct v4l2_buffer buf;
	struct list_head queue;
	wait_queue_head_t wait;
	enum uvc_buffer_state state;
};

struct uvc_video_queue {
	void *mem;
	unsigned int streaming;
	__u32 sequence;
	__u8 last_fid;

	unsigned int count;
	struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS];
	struct semaphore lock;
	spinlock_t irqlock;

	struct list_head mainqueue;
	struct list_head irqqueue;
};

struct uvc_video_device {
	struct uvc_device *dev;
	struct video_device *vdev;
	atomic_t count;

	enum uvc_stream_state stream;

	struct uvc_terminal *iterm;
	struct uvc_terminal *oterm;
	struct uvc_unit *processing;
	struct uvc_unit *extension[8];

	struct uvc_video_queue queue;

	struct uvc_streaming *streaming;

	struct urb *urb[UVC_URBS];
	char *iso_buffer[UVC_URBS];
};

enum uvc_device_state {
	UVC_DEV_DISCONNECTED = 1,
};

struct uvc_device {
	struct usb_device *udev;
	struct usb_interface *intf;
	int intfnum;

	enum uvc_device_state state;
	struct kref kref;

	/* Video control interface */
	__u16 uvc_version;
	__u32 clock_frequency;

	struct list_head terminals;
	struct list_head units;

	struct uvc_video_device video;

	/* Status Interrupt Endpoint */
	struct usb_host_endpoint *int_ep;
	struct urb *int_urb;
	__u8 status[16];

	/* Video Streaming interfaces */
	struct list_head streaming;
};

struct uvc_fh {
	struct uvc_device *video;
};

/* ------------------------------------------------------------------------
 * Forward declarations
 */

static DECLARE_MUTEX(dev_sem);

static struct usb_driver uvc_driver;

static void uvc_delete(struct kref *kref);

/* ------------------------------------------------------------------------
 * Control, formats, ...
 */

static struct uvc_menu_info power_line_frequency_controls[] = {
	{ 0, "Disabled" },
	{ 1, "50 Hz" },
	{ 2, "60 Hz" },
};

static struct uvc_control_desc uvc_ctrls[] = {
	{
		.v4l2		= V4L2_CID_BRIGHTNESS,
		.name		= "Brightness",
		.id		= PU_BRIGHTNESS_CONTROL,
		.bit		= 0,
		.size		= 2,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.unit		= UVC_GUID_UVC_PROCESSING,
		.flags		= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE,
	},
	{
		.v4l2		= V4L2_CID_CONTRAST,
		.name		= "Contrast",
		.id		= PU_CONTRAST_CONTROL,
		.bit		= 1,
		.size		= 2,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.unit		= UVC_GUID_UVC_PROCESSING,
		.flags		= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE,
	},
	{
		.v4l2		= V4L2_CID_HUE,
		.name		= "Hue",
		.id		= PU_HUE_CONTROL,
		.bit		= 2,
		.size		= 2,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.unit		= UVC_GUID_UVC_PROCESSING,
		.flags		= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE,
	},
	{
		.v4l2		= V4L2_CID_SATURATION,
		.name		= "Saturation",
		.id		= PU_SATURATION_CONTROL,
		.bit		= 3,
		.size		= 2,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.unit		= UVC_GUID_UVC_PROCESSING,
		.flags		= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE,
	},
	{
		.v4l2		= V4L2_CID_SHARPNESS,
		.name		= "Sharpness",
		.id		= PU_SHARPNESS_CONTROL,
		.bit		= 4,
		.size		= 2,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.unit		= UVC_GUID_UVC_PROCESSING,
		.flags		= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE,
	},
	{
		.v4l2		= V4L2_CID_GAMMA,
		.name		= "Gamma",
		.id		= PU_GAMMA_CONTROL,
		.bit		= 5,
		.size		= 2,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.unit		= UVC_GUID_UVC_PROCESSING,
		.flags		= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE,
	},
	{
		.v4l2		= V4L2_CID_BACKLIGHT_COMPENSATION,
		.name		= "Backlight Compensation",
		.id		= PU_BACKLIGHT_COMPENSATION_CONTROL,
		.bit		= 8,
		.size		= 2,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.unit		= UVC_GUID_UVC_PROCESSING,
		.flags		= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE,
	},
	{
		.v4l2		= V4L2_CID_GAIN,
		.name		= "Gain",
		.id		= PU_GAIN_CONTROL,
		.bit		= 9,
		.size		= 2,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.unit		= UVC_GUID_UVC_PROCESSING,
		.flags		= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE,
	},
	{
		.v4l2		= V4L2_CID_POWER_LINE_FREQUENCY,
		.name		= "Power Line Frequency",
		.id		= PU_POWER_LINE_FREQUENCY_CONTROL,
		.bit		= 10,
		.size		= 1,
		.type		= V4L2_CTRL_TYPE_MENU,
		.unit		= UVC_GUID_UVC_PROCESSING,
		.flags		= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE,
		.menu_info	= power_line_frequency_controls,
		.menu_count	= ARRAY_SIZE(power_line_frequency_controls),
	},
	{
		.v4l2		= V4L2_CID_HUE_AUTO,
		.name		= "Hue, Auto",
		.id		= PU_HUE_AUTO_CONTROL,
		.bit		= 11,
		.size		= 1,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
		.unit		= UVC_GUID_UVC_PROCESSING,
		.flags		= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
				| UVC_CONTROL_GET_DEF,
	},
	{
		.v4l2		= V4L2_CID_PANTILT_RELATIVE,
		.name		= "Pan/Tilt (relative)",
		.id		= LXU_MOTOR_PANTILT_RELATIVE_CONTROL,
		.bit		= 0,
		.size		= 4,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.unit		= UVC_GUID_LOGITECH_MOTOR,
		.flags		= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_MIN
				| UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_DEF,
	},
	{
		.v4l2		= V4L2_CID_PANTILT_RESET,
		.name		= "Pan/Tilt (reset)",
		.id		= LXU_MOTOR_PANTILT_RESET_CONTROL,
		.bit		= 0,
		.size		= 1,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.unit		= UVC_GUID_LOGITECH_MOTOR,
		.flags		= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_MIN
				| UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES
				| UVC_CONTROL_GET_DEF,
	},
	{
		.v4l2		= V4L2_CID_EXPOSURE_AUTO,
		.name		= "Exposure, Auto",
		.id		= CT_AE_MODE_CONTROL,
		.bit		= 1,
		.size		= 1,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.unit		= UVC_GUID_UVC_CAMERA,
		.flags		= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
				| UVC_CONTROL_GET_DEF,
	},
	{
		.v4l2		= V4L2_CID_EXPOSURE_ABSOLUTE,
		.name		= "Exposure (Absolute)",
		.id		= CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
		.bit		= 3,
		.size		= 4,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.unit		= UVC_GUID_UVC_CAMERA,
		.flags		= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_MIN
				| UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES
				| UVC_CONTROL_GET_DEF,
	},
	{
		.v4l2		= V4L2_CID_WHITE_BALANCE_TEMPERATURE_AUTO,
		.name		= "White Balance Temperature, Auto",
		.id		= PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL,
		.bit		= 12,
		.size		= 1,
		.type		= V4L2_CTRL_TYPE_BOOLEAN,
		.unit		= UVC_GUID_UVC_PROCESSING,
		.flags		= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR
				| UVC_CONTROL_GET_DEF,
	},
	{
		.v4l2		= V4L2_CID_WHITE_BALANCE_TEMPERATURE,
		.name		= "White Balance Temperature",
		.id		= PU_WHITE_BALANCE_TEMPERATURE_CONTROL,
		.bit		= 6,
		.size		= 2,
		.type		= V4L2_CTRL_TYPE_INTEGER,
		.unit		= UVC_GUID_UVC_PROCESSING,
		.flags		= UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_MIN
				| UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES
				| UVC_CONTROL_GET_DEF,
	}
};

static struct uvc_format_desc uvc_fmts[] = {
	{
		.guid		= UVC_GUID_FORMAT_YUY2,
		.fcc		= V4L2_PIX_FMT_YUYV,
	},
	{
		.guid		= UVC_GUID_FORMAT_YUY2A,
		.fcc		= V4L2_PIX_FMT_UYVY,
	},
	{
		.guid		= UVC_GUID_FORMAT_UYVY,
		.fcc		= V4L2_PIX_FMT_UYVY,
	},
	{
		.guid		= UVC_GUID_FORMAT_NV12,
		.fcc		= V4L2_PIX_FMT_NV12,
	},
};

static const __u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING;
static const __u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA;

/*
#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
*/

/* ------------------------------------------------------------------------
 * Debugging, printing and logging
 */

#define UVC_TRACE_PROBE		(1 << 0)
#define UVC_TRACE_DESCR		(1 << 1)
#define UVC_TRACE_CONTROL	(1 << 2)
#define UVC_TRACE_FORMAT	(1 << 3)
#define UVC_TRACE_CAPTURE	(1 << 4)
#define UVC_TRACE_CALLS		(1 << 5)
#define UVC_TRACE_IOCTL		(1 << 6)
#define UVC_TRACE_FRAME		(1 << 7)

static unsigned int trace = 0;

#define uvc_trace(flag, msg...) \
	do { \
		if (trace & flag) \
			printk(KERN_DEBUG "uvcvideo: " msg); \
	} while(0)

#define uvc_printk(level, msg...) \
	printk(level "uvcvideo: " msg)

#if 0
static void uvc_print_streaming_control(struct uvc_streaming_control *ctrl)
{
	printk(KERN_DEBUG "bmHint:                      0x%04x\n", ctrl->bmHint);
	printk(KERN_DEBUG "bFormatIndex:                   %3u\n", ctrl->bFormatIndex);
	printk(KERN_DEBUG "bFrameIndex:                    %3u\n", ctrl->bFrameIndex);
	printk(KERN_DEBUG "dwFrameInterval:          %9u\n", ctrl->dwFrameInterval);
	printk(KERN_DEBUG "wKeyFrameRate:                %5u\n", ctrl->wKeyFrameRate);
	printk(KERN_DEBUG "wPFrameRate:                  %5u\n", ctrl->wPFrameRate);
	printk(KERN_DEBUG "wCompQuality:                 %5u\n", ctrl->wCompQuality);
	printk(KERN_DEBUG "wCompWindowSize:              %5u\n", ctrl->wCompWindowSize);
	printk(KERN_DEBUG "wDelay:                       %5u\n", ctrl->wDelay);
	printk(KERN_DEBUG "dwMaxVideoFrameSize:      %9u\n", ctrl->dwMaxVideoFrameSize);
	printk(KERN_DEBUG "dwMaxPayloadTransferSize: %9u\n", ctrl->dwMaxPayloadTransferSize);
	printk(KERN_DEBUG "dwClockFrequency:         %9u\n", ctrl->dwClockFrequency);
	printk(KERN_DEBUG "bmFramingInfo:                 0x%02x\n", ctrl->bmFramingInfo);
	printk(KERN_DEBUG "bPreferedVersion:               %3u\n", ctrl->bPreferedVersion);
	printk(KERN_DEBUG "bMinVersion:                    %3u\n", ctrl->bMinVersion);
	printk(KERN_DEBUG "bMaxVersion:                    %3u\n", ctrl->bMaxVersion);
}
#endif

/* ------------------------------------------------------------------------
 * Utility functions
 */

static inline int uvc_get_bit(const __u8 *data, int bit)
{
	return (data[bit >> 3] >> (bit & 7)) & 1;
}

static __s32 uvc_get_le_value(const __u8 *data, int size)
{
	switch (size) {
	case 1:
		return data[0];

	case 2:
		return le16_to_cpup((__le16*)data);

	case 4:
		return le32_to_cpup((__le32*)data);
	}

	return 0;
}

static void uvc_set_le_value(__s32 value, __u8 *data, int size)
{
	switch (size) {
	case 1:
		data[0] = value;
		break;

	case 2:
		*(__le16*)&data[0] = cpu_to_le16(value);
		break;

	case 4:
		*(__le32*)&data[0] = cpu_to_le32(value);
		break;
	}
}

static 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 __u32 uvc_guid_to_fcc(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].fcc;
	}

	return -1;
}

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.
 */
static 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.
	 */
	memset(an, 0, sizeof an);
	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.
 */
static uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator)
{
	uint32_t multiplier;

	/* Saturate the result if the operation would overflow. */
	if (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_terminal *uvc_terminal(struct uvc_device *dev, int id)
{
	struct list_head *p;

	list_for_each(p, &dev->terminals) {
		struct uvc_terminal *terminal;
		terminal = list_entry(p, struct uvc_terminal, list);
		if (terminal->id == id)
			return terminal;
	}

	return NULL;
}

static struct uvc_unit *uvc_unit_by_id(struct uvc_device *dev, int id)
{
	struct list_head *p;

	list_for_each(p, &dev->units) {
		struct uvc_unit *unit;
		unit = list_entry(p, struct uvc_unit, list);
		if (unit->id == id)
			return unit;
	}

	return NULL;
}

static struct uvc_unit *uvc_unit_by_guid(struct uvc_device *dev, __u8 guid[16])
{
	struct list_head *p;

	list_for_each(p, &dev->units) {
		struct uvc_unit *unit;
		unit = list_entry(p, struct uvc_unit, list);
		if (unit->type == VC_EXTENSION_UNIT &&
		    memcmp(unit->extension.guidExtensionCode, guid, 16) == 0)
			return unit;
	}

	return NULL;
}

/* ------------------------------------------------------------------------
 * 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 help (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.
 *
 */

/*
 * Free the video buffers.
 *
 * This function must be called with the queue lock held.
 */
static 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) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
		unsigned long addr = (unsigned long)queue->mem;
		size_t size = queue->count * queue->buffer[0].size;
		while (size > 0) {
			ClearPageReserved(vmalloc_to_page((void*)addr));
			addr += PAGE_SIZE;
			size -= PAGE_SIZE;
		}
#endif
		vfree(queue->mem);
		queue->count = 0;
	}

	return 0;
}

/*
 * 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.
 */
static 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;

	down(&queue->lock);

	if ((ret = uvc_free_buffers(queue)) < 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].size = bufsize;
		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;
	ret = nbuffers;

done:
	up(&queue->lock);
	return ret;
}

/*
 * 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_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);
}

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;
	}
}

/*
 * Dequeue a video buffer. If nonblocking is false, block until a buffer is
 * available.
 */
static 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;
	}

	down(&queue->lock);
	if (list_empty(&queue->mainqueue)) {
		uvc_trace(UVC_TRACE_CAPTURE, "[E] Empty buffer queue.\n");
		ret = -EINVAL;
		goto done;
	}

	buf = list_entry(queue->mainqueue.next, struct uvc_buffer, stream);
	uvc_trace(UVC_TRACE_CAPTURE, "Dequeuing buffer %u.\n", buf->buf.index);
	if ((ret = uvc_queue_waiton(buf, nonblocking)) < 0)
		goto done;

	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:
	up(&queue->lock);
	return ret;
}

/*
 * Queue a video buffer. Attempting to queue a buffer that has already been
 * queued will return -EINVAL.
 */
static 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;
	}

	down(&queue->lock);
	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;
	}

	buf->state = UVC_BUF_STATE_QUEUED;
	buf->buf.bytesused = 0;
	list_add_tail(&buf->stream, &queue->mainqueue);
	spin_lock_irqsave(&queue->irqlock, flags);
	list_add_tail(&buf->queue, &queue->irqqueue);
	spin_unlock_irqrestore(&queue->irqlock, flags);

done:
	up(&queue->lock);
	return ret;
}

static void uvc_queue_init(struct uvc_video_queue *queue)
{
	init_MUTEX(&queue->lock);
	spin_lock_init(&queue->irqlock);
	INIT_LIST_HEAD(&queue->mainqueue);
	INIT_LIST_HEAD(&queue->irqqueue);
}

/*
 * 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.
 *
 * This function acquires the irq spinlock and can be called from interrupt
 * context.
 */
static void uvc_queue_cancel(struct uvc_video_queue *queue)
{
	struct uvc_buffer *buf;
	unsigned long flags;

	spin_lock_irqsave(&queue->irqlock, flags);
	while (!list_empty(&queue->irqqueue)) {
		buf = list_entry(queue->irqqueue.next, struct uvc_buffer, queue);
		list_del(&buf->queue);
		buf->state = UVC_BUF_STATE_ERROR;
		wake_up(&buf->wait);
	}
	spin_unlock_irqrestore(&queue->irqlock, flags);
}

/*
 * 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.
 */
static int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
{
	unsigned int i;
	int ret = 0;

	down(&queue->lock);
	if (enable) {
		if (queue->streaming) {
			ret = -EBUSY;
			goto done;
		}
		queue->last_fid = -1;
		queue->sequence = 0;
		queue->streaming = 1;
	}
	else {
		uvc_queue_cancel(queue);
		INIT_LIST_HEAD(&queue->mainqueue);

		for (i = 0; i < queue->count; ++i)
			queue->buffer[i].state = UVC_BUF_STATE_IDLE;

		queue->streaming = 0;
	}

done:
	up(&queue->lock);
	return ret;
}

static struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
		struct uvc_buffer *buf)
{
	struct uvc_buffer *nextbuf;
	unsigned long flags;

	spin_lock_irqsave(&queue->irqlock, flags);
	list_del(&buf->queue);
	if (!list_empty(&queue->irqqueue))
		nextbuf = list_entry(queue->irqqueue.next, 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;
}

/* ------------------------------------------------------------------------
 * 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)

/*
 * 
 */
static int uvc_video_decode(struct uvc_video_queue *queue,
		struct uvc_buffer *buf, const __u8 *data, unsigned int len)
{
	unsigned int maxlen, nbytes;
	__u8 *mem;
	__u8 fid = queue->last_fid;
	int hlen = 0, flags = 0, is_header = 0;
	static const __u8 hdr[] = { 0x11, 0x22, 0x33, 0x44,
				    0xde, 0xad, 0xbe, 0xef,
				    0xde, 0xad, 0xfa, 0xce };

	/* 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 >= 14 && memcmp (&data[3], hdr, 12) == 0) ||
	    (len >= 13 && memcmp (&data[2], hdr, 12) == 0)) {
		uvc_trace(UVC_TRACE_FRAME, "Detecting new header");
		hlen = (data[3] == 0x11) ? data[1] : data[0];
		if (hlen > len - 1 || hlen < 2)
			return -EINVAL;
		flags = (data[3] == 0x11) ? data[2] : data[1];
		is_header = 1;
	}

	/* Skip payloads marked with the error bit ("error frames"). */
	if (hlen != 0 && flags & UVC_STREAM_ERR) {
		uvc_trace(UVC_TRACE_FRAME, "Dropping packet (error bit set).\n");

		//return 0;
	}

	if (hlen != 0) {
		fid = flags & UVC_STREAM_FID;
	}

	/* Store the packet FID bit and return immediately when the buffer is
	 * NULL.
	 */
	if (buf == NULL) {
		queue->last_fid = fid;
		return 0;
	}

	/* 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 (buf->state != UVC_BUF_STATE_ACTIVE) {
		if (fid == queue->last_fid) {
			uvc_trace(UVC_TRACE_FRAME, "Dropping packet (out of "
				"sync).\n");
			return 0;
		}

		/* 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 packet 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 packet had the EOF bit set.
	 */
	if (fid != queue->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;
	} else if (is_header && buf->buf.bytesused > 0) {
		buf->state = UVC_BUF_STATE_DONE;
		return -EAGAIN;
	}

	queue->last_fid = fid;

	/* Copy the video data to the buffer. */
	len -= hlen;
	maxlen = buf->buf.length - buf->buf.bytesused;
	if (!is_header) { /* we skip headers, they do not contain data */
		mem = queue->mem + buf->buf.m.offset + buf->buf.bytesused;
		nbytes = min(len - hlen, maxlen);
		memmove(mem, data + hlen, nbytes);
		buf->buf.bytesused += nbytes;
	}

	/* Drop the current frame if the buffer size was exceeded. */
	if (len - hlen > maxlen || buf->buf.bytesused == buf->buf.length) {
		uvc_trace(UVC_TRACE_FRAME, "Frame complete (overflow).\n");
		buf->state = UVC_BUF_STATE_DONE;
	}

	/* Mark the buffer as done if the EOF marker is set. */
	if (hlen != 0 && (flags & UVC_STREAM_EOF && buf->buf.bytesused != 0)) {
		uvc_trace(UVC_TRACE_FRAME, "Frame complete (EOF found).\n");
		if (hlen != 0 && hlen == len)
			printk("EOF in empty packet.\n");
		buf->state = UVC_BUF_STATE_DONE;
	}

	return 0;
}

/* ------------------------------------------------------------------------
 * URB handling
 */

/*
 * Completion handler for status URB.
 */
static void uvc_status_complete(struct urb *urb, struct pt_regs *regs)
{
	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 >= 4 && (dev->status[0] & 0x0f) == 2) {
		if (dev->status[2] == 0)
			uvc_printk(KERN_DEBUG, "Button event (%d).\n",
				dev->status[3]);
	}

	/* 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);
	}
}

/*
 * Completion handler for isochronous URBs.
 */
static void uvc_video_complete(struct urb *urb, struct pt_regs *regs)
{
	struct uvc_video_device *video = urb->context;
	struct uvc_video_queue *queue = &video->queue;
	struct uvc_buffer *buf = NULL;
	unsigned long flags;
	int ret, i;

	switch (urb->status) {
	case 0:
		break;

	default:
		uvc_printk(KERN_WARNING, "Non-zero status (%d) in isoc "
			"completion handler.\n", urb->status);

	case -ENOENT:		/* usb_kill_urb() called. */
	case -ECONNRESET:	/* usb_unlink_urb() called. */
	case -ESHUTDOWN:	/* The endpoint is being disabled. */
		uvc_queue_cancel(queue);
		return;
	}

	spin_lock_irqsave(&queue->irqlock, flags);
	if (!list_empty(&queue->irqqueue))
		buf = list_entry(queue->irqqueue.next, struct uvc_buffer, queue);
	spin_unlock_irqrestore(&queue->irqlock, flags);

	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 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 = uvc_video_decode(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(queue, buf);
		} while (ret == -EAGAIN);
	}

	if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
		uvc_printk(KERN_ERR, "Failed to resubmit isoc URB (%d).\n",
			ret);
        }
}

/*
 * Uninitialize isochronous URBs and free transfer buffers.
 */
static void uvc_uninit_isoc(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->iso_buffer[i]) {
			usb_buffer_free(video->dev->udev,
				urb->transfer_buffer_length,
				video->iso_buffer[i], urb->transfer_dma);
			video->iso_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_isoc(struct uvc_video_device *video, struct usb_host_endpoint *ep)
{
	struct urb *urb;
	unsigned int npackets, i, j;
	__u16 psize;
	__u32 size;
	int ret;

	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_isoc(video);
			return -ENOMEM;
		}

		video->iso_buffer[i] = usb_buffer_alloc(video->dev->udev,
			size, GFP_KERNEL, &urb->transfer_dma);
		if (video->iso_buffer[i] == NULL) {
			usb_free_urb(urb);
			uvc_uninit_isoc(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->iso_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;
	}

	/* 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 isoc URB %u "
					"(%d).\n", i, ret);
			uvc_uninit_isoc(video);
			return ret;
		}
	}

	return 0;
}

/* ------------------------------------------------------------------------
 * UVC Controls
 */

static int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
			  __u8 intfnum, __u8 cs, void *data, __u16 size)
{
	__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, UVC_CTRL_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;
}

static struct uvc_control_desc *uvc_find_control(struct uvc_video_device *video,
	__u32 v4l2_id, __u8 *unit_id)
{
	struct uvc_unit *unit = NULL;
	struct uvc_control_desc *ctrl = uvc_ctrls;
	struct uvc_control_desc *end = ctrl + ARRAY_SIZE(uvc_ctrls);
	__u16 size;
	__u8 *bitmask;
	__u8 id = 0;

	/* Find the control. */
	for (; ctrl < end; ++ctrl) {
		if (ctrl->v4l2 != v4l2_id)
			continue;

		if (memcmp(ctrl->unit, uvc_processing_guid, 16) == 0)
		{
			if (video->processing == NULL)
				continue;

			bitmask = video->processing->processing.bmControls;
			size = video->processing->processing.bControlSize << 3;
			id = video->processing->id;
		}
		else if (memcmp(ctrl->unit, uvc_camera_guid, 16) == 0)
		{
			if (video->iterm->type != ITT_CAMERA)
				continue;

			bitmask = video->iterm->camera.bmControls;
			size = video->iterm->camera.bControlSize << 3;
			id = video->iterm->id;
		}
		else
		{
			unit = uvc_unit_by_guid(video->dev, ctrl->unit);
			if (unit == NULL)
				continue;

			bitmask = unit->extension.bmControls;
			size = unit->extension.bControlSize << 3;
			id = unit->id;
		}

		if (ctrl->bit < size && uvc_get_bit(bitmask, ctrl->bit))
			break;
	}

	if (ctrl == end) {
		uvc_trace(UVC_TRACE_CONTROL, "Control 0x%08x not found.\n",
			v4l2_id);
		return NULL;
	}

	if (unit_id != NULL)
		*unit_id = id;

	return ctrl;
}

static int uvc_get_v4l2_ctrl(struct uvc_video_device *video, struct v4l2_control *v4l2_ctrl)
{
	struct uvc_control_desc *ctrl;
	__u8 unit_id;
	__u8 data[4];
	int ret;

	ctrl = uvc_find_control(video, v4l2_ctrl->id, &unit_id);
	if (ctrl == NULL)
		return -EINVAL;

	ret = uvc_query_ctrl(video->dev, GET_CUR, unit_id, video->dev->intfnum,
				ctrl->id, &data, ctrl->size);
	if (ret < 0)
		return ret;

	v4l2_ctrl->value = uvc_get_le_value(data, ctrl->size);
	return 0;
}

static int uvc_set_v4l2_ctrl(struct uvc_video_device *video, struct v4l2_control *v4l2_ctrl)
{
	struct uvc_control_desc *ctrl;
	__u8 unit_id;
	__u8 data[4];

	ctrl = uvc_find_control(video, v4l2_ctrl->id, &unit_id);
	if (ctrl == NULL)
		return -EINVAL;

	uvc_set_le_value(v4l2_ctrl->value, data, ctrl->size);

	return uvc_query_ctrl(video->dev, SET_CUR, unit_id, video->dev->intfnum,
				ctrl->id, &data, ctrl->size);
}

static int uvc_query_v4l2_ctrl(struct uvc_video_device *video, struct v4l2_queryctrl *v4l2_ctrl)
{
	struct uvc_control_desc *ctrl;
	__u8 unit_id;
	__u8 data[4];
	int ret = 0;

	ctrl = uvc_find_control(video, v4l2_ctrl->id, &unit_id);
	if (ctrl == NULL)
		return -EINVAL;

	if (ctrl->flags & UVC_CONTROL_GET_DEF) {
		ret |= uvc_query_ctrl(video->dev, GET_DEF, unit_id,
					video->dev->intfnum, ctrl->id, &data,
					ctrl->size);
		v4l2_ctrl->default_value = uvc_get_le_value(data, ctrl->size);
	}
	if (ctrl->flags & UVC_CONTROL_GET_MIN) {
		ret |= uvc_query_ctrl(video->dev, GET_MIN, unit_id,
					video->dev->intfnum, ctrl->id, &data,
					ctrl->size);
		v4l2_ctrl->minimum = uvc_get_le_value(data, ctrl->size);
	}
	if (ctrl->flags & UVC_CONTROL_GET_MAX) {
		ret |= uvc_query_ctrl(video->dev, GET_MAX, unit_id,
					video->dev->intfnum, ctrl->id, &data,
					ctrl->size);
		v4l2_ctrl->maximum = uvc_get_le_value(data, ctrl->size);
	}
	if (ctrl->flags & UVC_CONTROL_GET_RES) {
		ret |= uvc_query_ctrl(video->dev, GET_RES, unit_id,
					video->dev->intfnum, ctrl->id, &data,
					ctrl->size);
		v4l2_ctrl->step = uvc_get_le_value(data, ctrl->size);
	}

	if (ret != 0)
		return -EIO;

	v4l2_ctrl->type = ctrl->type;
	strncpy(v4l2_ctrl->name, ctrl->name, sizeof v4l2_ctrl->name);
	v4l2_ctrl->flags = 0;

	return 0;
}

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 > 0x0100 ? 34 : 26;
	ret = uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum,
		probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, &data, size);

	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_cpup((__le32*)&data[18]);
	ctrl->dwMaxPayloadTransferSize = le32_to_cpup((__le32*)&data[22]);

	if (size == 34) {
		ctrl->dwClockFrequency = le32_to_cpup((__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;
	}

	return 0;
}

static 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 > 0x0100 ? 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. */
	*(__le32*)&data[18] = cpu_to_le32(ctrl->dwMaxVideoFrameSize);
	*(__le32*)&data[22] = cpu_to_le32(ctrl->dwMaxPayloadTransferSize);

	if (size == 34) {
		*(__le32*)&data[26] = cpu_to_le32(ctrl->dwClockFrequency);
		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);
}

/*
 * 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_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;

	down(&video->streaming->lock);

	/* 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 ((ret = uvc_get_video_ctrl(video, &probe_min, 1, GET_MIN)) < 0 ||
	    (ret = uvc_get_video_ctrl(video, &probe_max, 1, GET_MAX)) < 0) {
		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;

		bandwidth = probe->dwMaxPayloadTransferSize;
		if (bandwidth <= video->streaming->maxpsize)
			break;

		/* 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:
	up(&video->streaming->lock);
	return ret;
}

/* ------------------------------------------------------------------------
 * Video device
 */

/*
 * Initialize the UVC video device.
 *
 * This function is called before registering the device with V4L.
 */
static int uvc_video_init(struct uvc_video_device *video)
{
	struct uvc_streaming_control *probe = &video->streaming->ctrl;
	struct uvc_format *format = &video->streaming->format[0];
	struct uvc_frame *frame = &format->frame[0];
	int ret;

	if (format->nframes == 0) {
		uvc_printk(KERN_WARNING, "Device initialization failed: the "
			"default format has no frame descriptors.\n");
		return -EINVAL;
	}

	/* Set the format index, frame index and frame interval. */
	memset(probe, 0, sizeof *probe);
	probe->bmHint = 0;
	probe->bFormatIndex = format->index;
	probe->bFrameIndex = frame->bFrameIndex;
	probe->dwFrameInterval = frame->dwDefaultFrameInterval;

	if ((ret = uvc_set_video_ctrl(video, probe, 1)) < 0)
		return ret;

	/* Retrieve the default format and commit it. */
	if ((ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0)
		return ret;
	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->count, 1);

	return 0;
}

/*
 * Enable or disable the video stream.
 */
static int uvc_video_enable(struct uvc_video_device *video, int enable)
{
	struct usb_interface *intf = video->streaming->intf;
	struct usb_host_interface *alts;
	struct usb_host_endpoint *ep;
	int intfnum = video->streaming->intfnum;
	unsigned int bandwidth, psize, i;
	int ret;

	if (!enable) {
		uvc_uninit_isoc(video);
		usb_set_interface(video->dev->udev, intfnum, 0);
		uvc_queue_enable(&video->queue, 0);
		return 0;
	}

	if ((ret = uvc_queue_enable(&video->queue, 1)) < 0) 
		return ret;

	/* Select the alternate setting. */
	bandwidth = video->streaming->ctrl.dwMaxPayloadTransferSize;

	for (i = 0; i < intf->num_altsetting; ++i) {
		alts = &intf->altsetting[i];
		ep = uvc_find_endpoint(alts,
				video->streaming->input.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 ||
	    (ret = uvc_init_isoc(video, ep)) < 0)
		return ret;

	return 0;
}

/*
 * 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_get_ctrl(struct uvc_video_device *video, struct v4l2_control *ctrl)
{
	switch (ctrl->id) {
	case V4L2_CID_AUTO_WHITE_BALANCE:
	case V4L2_CID_DO_WHITE_BALANCE:
	case V4L2_CID_RED_BALANCE:
	case V4L2_CID_BLUE_BALANCE:
		return -EINVAL;

	default:
		return uvc_get_v4l2_ctrl(video, ctrl);
	}
}

static int uvc_v4l2_set_ctrl(struct uvc_video_device *video, struct v4l2_control *ctrl)
{
	switch (ctrl->id) {
	case V4L2_CID_AUTO_WHITE_BALANCE:
	case V4L2_CID_DO_WHITE_BALANCE:
	case V4L2_CID_RED_BALANCE:
	case V4L2_CID_BLUE_BALANCE:
		return -EINVAL;

	default:
		return uvc_set_v4l2_ctrl(video, ctrl);
	}
}

static int uvc_v4l2_query_ctrl(struct uvc_video_device *video, struct v4l2_queryctrl *ctrl)
{
	switch (ctrl->id) {
	case V4L2_CID_AUTO_WHITE_BALANCE:
	case V4L2_CID_DO_WHITE_BALANCE:
	case V4L2_CID_RED_BALANCE:
	case V4L2_CID_BLUE_BALANCE:
		return -EINVAL;

	default:
		return uvc_query_v4l2_ctrl(video, ctrl);
	}
}

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_desc *ctrl;
	__u32 i;
	
	ctrl = uvc_find_control(video, query_menu->id, NULL);
	if (ctrl == NULL || ctrl-> type != V4L2_CTRL_TYPE_MENU)
		return -EINVAL;
	
	menu_info = ctrl->menu_info;

	for (i = 0; i < ctrl->menu_count; ++i, ++menu_info) {
		if (query_menu->index == menu_info->index) {
			strncpy(query_menu->name, menu_info->name, 32);
			return 0;
		}
	}

	return -EINVAL;
}

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);

	/* 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;
	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;
	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;

	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;

	if (probe.dwMaxVideoFrameSize == 0)
		probe.dwMaxVideoFrameSize =
			video->streaming->format[probe.bFormatIndex - 1].
				frame[probe.bFrameIndex - 1].dwMaxVideoFrameBufferSize;

	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;

	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;
}

/* ------------------------------------------------------------------------
 * V4L2 file operations
 */

static int uvc_v4l2_open(struct inode *inode, struct file *file)
{
	struct video_device *vdev;
	struct uvc_video_device *video;

	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n");
	down(&dev_sem);
	vdev = video_devdata(file);
	video = video_get_drvdata(vdev);

	if (video->dev->state & UVC_DEV_DISCONNECTED) {
		up(&dev_sem);
		return -ENODEV;
	}

	if (!atomic_dec_and_test(&video->count)) {
		atomic_inc(&video->count);
		up(&dev_sem);
		return -EBUSY;
	}

	kref_get(&video->dev->kref);
	up(&dev_sem);

	return 0;
}

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);

	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n");
	uvc_video_enable(video, 0);

	down(&video->queue.lock);
	if (uvc_free_buffers(&video->queue) < 0)
		uvc_printk(KERN_ERR, "uvc_v4l2_release: Unable to free buffers.\n");
	up(&video->queue.lock);

	atomic_inc(&video->count);
	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);
	int ret;

	if (trace & UVC_TRACE_IOCTL) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
		switch (_IOC_TYPE(cmd)) {
		case 'v':
			uvc_printk(KERN_DEBUG, "ioctl 0x%x (V4L1)\n", cmd);
			break;
		case 'V':
			uvc_printk(KERN_DEBUG, "ioctl 0x%x (%s)\n",
				cmd, v4l2_ioctl_names[_IOC_NR(cmd)]);
			break;
		default:
			uvc_printk(KERN_DEBUG, "ioctl 0x%x (???)\n", cmd);
			break;
		}
#else
		v4l_printk_ioctl(cmd);
#endif
	}

	switch (cmd) {
	/* Query capabilities */
	case VIDIOC_QUERYCAP:
	{
		struct v4l2_capability *cap = arg;

		memset(cap, 0, sizeof *cap);
		strncpy(cap->driver, "uvcvideo", sizeof cap->driver);
		if (video->dev->udev->product)
			strncpy(cap->card, video->dev->udev->product, sizeof cap->card);
		else
			strncpy(cap->card, "USB Video Class device", sizeof cap->card);
		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_v4l2_query_ctrl(video, (struct v4l2_queryctrl*)arg);

	case VIDIOC_G_CTRL:
		return uvc_v4l2_get_ctrl(video, (struct v4l2_control*)arg);

	case VIDIOC_S_CTRL:
		return uvc_v4l2_set_ctrl(video, (struct v4l2_control*)arg);

	case VIDIOC_QUERYMENU:
		return uvc_v4l2_query_menu(video, (struct v4l2_querymenu*)arg);

	/* Get, Set & Enum input */
	case VIDIOC_ENUMINPUT:
	{
		struct v4l2_input *input = arg;

		if (input->index != 0)
			return -EINVAL;

		memset(input, 0, sizeof *input);
		strncpy(input->name, "Camera", sizeof input->name);
		input->type = V4L2_INPUT_TYPE_CAMERA;
	}
		break;

	case VIDIOC_G_INPUT:
		*(int*)arg = 0;
		break;

	case VIDIOC_S_INPUT:
		if (*(int*)arg != 0)
			return -EINVAL;

		break;

	/* 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 = format->flags;
		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;
		return uvc_v4l2_try_format(video, (struct v4l2_format*)arg,
						&probe, NULL, NULL);
	}

	case VIDIOC_S_FMT:
		return uvc_v4l2_set_format(video, (struct v4l2_format*)arg);

	case VIDIOC_G_FMT:
		return uvc_v4l2_get_format(video, (struct v4l2_format*)arg);

	/* Get & Set streaming parameters */
	case VIDIOC_G_PARM:
		return uvc_v4l2_get_streamparm(video, (struct v4l2_streamparm*)arg);

	case VIDIOC_S_PARM:
		return uvc_v4l2_set_streamparm(video, (struct v4l2_streamparm*)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;
		
		return 0;
	}

	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_alloc_buffers(&video->queue, rb->count, bufsize)) < 0)
			return ret;

		rb->count = ret;
	}
		break;

	case VIDIOC_QUERYBUF:
	{
		struct v4l2_buffer *buf = arg;
		struct uvc_buffer *ubuf;

		if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
		    buf->index >= video->queue.count)
			return -EINVAL;

		ubuf = &video->queue.buffer[buf->index];
		uvc_query_buffer(ubuf, buf);
	}
		break;

	case VIDIOC_QBUF:
		return uvc_queue_buffer(&video->queue,
			(struct v4l2_buffer*)arg);

	case VIDIOC_DQBUF:
		return uvc_dequeue_buffer(&video->queue,
			(struct v4l2_buffer*)arg, file->f_flags & O_NONBLOCK);

	case VIDIOC_STREAMON:
	{
		int *type = arg;

		if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
			return -EINVAL;

		if ((ret = uvc_video_enable(video, 1)) < 0)
			return ret;

		video->stream = UVC_STREAM_ON;
	}
		break;

	case VIDIOC_STREAMOFF:
	{
		int *type = arg;

		if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
			return -EINVAL;

		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;

	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 0;
}

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;
}

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 = NULL;
	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;

	down(&video->queue.lock);

	for (i = 0; i < video->queue.count; ++i) {
		if ((video->queue.buffer[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) {
			buffer = &video->queue.buffer[i];
			break;
		}
	}

	if (buffer == NULL || size != buffer->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;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
	/* Not sure if this is needed. remap_pfn_range() sets VM_RESERVED
	 * in 2.6.14.
	 */
	vma->vm_flags |= VM_RESERVED;
#endif

	addr = (unsigned long)video->queue.mem + buffer->buf.m.offset;
	while (size > 0) {
		page = vmalloc_to_page((void*)addr);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
                SetPageReserved(page);
		if ((ret = remap_pfn_range(vma, start, page_to_pfn(page),
					PAGE_SIZE, vma->vm_page_prot)) < 0)
#else
		if ((ret = vm_insert_page(vma, start, page)) < 0)
#endif
			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:
	up(&video->queue.lock);
	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);
	struct uvc_buffer *buf;
	unsigned int mask = 0;

	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n");

	down(&video->queue.lock);
	if (list_empty(&video->queue.mainqueue)) {
		mask |= POLLERR;
		goto done;
	}
	buf = list_entry(video->queue.mainqueue.next, 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:
	up(&video->queue.lock);
	return mask;
}

static struct file_operations uvc_fops = {
	.owner		= THIS_MODULE,
	.open		= uvc_v4l2_open,
	.release	= uvc_v4l2_release,
	.ioctl		= uvc_v4l2_ioctl,
	.llseek		= no_llseek,
	.read		= uvc_v4l2_read,
	.mmap		= uvc_v4l2_mmap,
	.poll		= uvc_v4l2_poll,
};

/* ------------------------------------------------------------------------
 * 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_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:
		if (buflen < 27) {
			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
			       "interface %d FORMAT error\n",
			       dev->udev->devnum,
			       alts->desc.bInterfaceNumber);
			return -EINVAL;
		}

		strncpy(format->name, "Uncompressed", sizeof format->name);
		format->fcc = uvc_guid_to_fcc(&buffer[5]);
		format->flags = 0;
		format->bpp = 16;
		ftype = VS_FRAME_UNCOMPRESSED;
		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 = V4L2_FMT_FLAG_COMPRESSED;
		format->bpp = 0;
		ftype = VS_FRAME_MJPEG;
		break;

	case VS_FORMAT_MPEG2TS:
	case VS_FORMAT_DV:
	case VS_FORMAT_FRAME_BASED:
	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];

	while (buflen > 2 && buffer[2] == ftype) {
		frame = &format->frame[format->nframes];

		switch (buffer[2]) {
		case VS_FRAME_UNCOMPRESSED:
		case VS_FRAME_MJPEG:
			n = buflen > 25 ? buffer[25] : 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]);
			frame->dwMaxVideoFrameBufferSize = le32_to_cpup((__le32*)&buffer[17]);
			frame->bFrameIntervalType = buffer[25];
			frame->dwFrameInterval = *intervals;

			/* 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;
			interval = le32_to_cpup((__le32*)&buffer[21]);
			if (interval < frame->dwFrameInterval[0])
				interval = frame->dwFrameInterval[0];
			else if (interval > frame->dwFrameInterval[n])
				interval = frame->dwFrameInterval[n];

			frame->dwDefaultFrameInterval = interval;

			uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n",
				frame->wWidth, frame->wHeight,
				10000000/frame->dwDefaultFrameInterval,
				(100000000/frame->dwDefaultFrameInterval)%10);

			break;
		}

		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 uvc_streaming *streaming)
{
	struct uvc_format *format;
	struct uvc_frame *frame;
	struct usb_interface *intf = streaming->intf;
	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;

	/* Skip the standard interface descriptors. */
	while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) {
		buflen -= buffer[0];
		buffer += buffer[0];
	}

	if (buflen <= 2)
		return -EINVAL;

	/* 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);
		return -EINVAL;
	}
	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);
			return -EINVAL;
		}

		streaming->input.bNumFormats = p;
		streaming->input.bEndpointAddress = buffer[6];
		streaming->input.bmInfo = buffer[7];
		streaming->input.bTerminalLink = buffer[8];
		streaming->input.bStillCaptureMethod = buffer[9];
		streaming->input.bTriggerSupport = buffer[10];
		streaming->input.bTriggerUsage = buffer[11];
		streaming->input.bControlSize = n;

		streaming->input.bmaControls = kmalloc(p*n, GFP_KERNEL);
		if (streaming->input.bmaControls == NULL)
			return -ENOMEM;

		memcpy(streaming->input.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);
		return -EINVAL;
	}

	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:
			nformats++;
			break;

		case VS_FORMAT_MPEG2TS:
		case VS_FORMAT_DV:
		case VS_FORMAT_FRAME_BASED:
		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);
		return -EINVAL;
	}

	size = nformats * sizeof *format + nframes * sizeof *frame
	     + nintervals * sizeof *interval;
	format = kmalloc(size, GFP_KERNEL);
	if (format == NULL)
		return -ENOMEM;
	memset(format, 0, size);

	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:
			format->frame = frame;
			ret = uvc_parse_format(dev, streaming, format,
				&interval, buffer, buflen);
			if (ret < 0)
				return ret;

			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->input.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;
	}

	return 0;
}

/* 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 uvc_unit *unit;
	unsigned int n, p;
	int handled = 0;

	switch (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,
			       dev->intf->cur_altsetting->desc.bInterfaceNumber);
			break;
		}

		unit = kmalloc(sizeof *unit + p + 2*n, GFP_KERNEL);
		if (unit == NULL)
			return -ENOMEM;
		memset(unit, 0, sizeof *unit + p + 2*n);

		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->units);
		handled = 1;
		break;
	}

	return handled;
}

static int uvc_parse_control(struct uvc_device *dev)
{
	struct usb_device *udev = dev->udev;
	struct uvc_streaming *streaming;
	struct uvc_terminal *term;
	struct uvc_unit *unit;
	struct usb_interface *intf;
	struct usb_host_interface *alts = dev->intf->cur_altsetting;
	unsigned char *buffer = alts->extra;
	int buflen = alts->extralen;
	unsigned int i, n, p;
	__u16 type;

	/* 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;

		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,
				       dev->intf->cur_altsetting->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;
				}

				if (usb_interface_claimed(intf)) {
					uvc_trace(UVC_TRACE_DESCR, "device %d "
						"interface %d is already claimed\n",
						udev->devnum, i);
					continue;
				}

				usb_driver_claim_interface(&uvc_driver, intf, (void*)-1);

				streaming = kmalloc(sizeof *streaming, GFP_KERNEL);
				if (streaming == NULL)
					continue;
				memset(streaming, 0, sizeof *streaming);
				init_MUTEX(&streaming->lock);
				streaming->intf = intf;
				streaming->intfnum =
					intf->cur_altsetting->desc.bInterfaceNumber;

				if (uvc_parse_streaming(dev, streaming) < 0) {
					kfree(streaming);
					continue;
				}

				list_add_tail(&streaming->list, &dev->streaming);
			}
			break;

		case VC_INPUT_TERMINAL:
			if (buflen < 8) {
				uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
				       "interface %d INPUT_TERMINAL error\n", udev->devnum,
				       dev->intf->cur_altsetting->desc.bInterfaceNumber);
				return -EINVAL;
			}

			type = le16_to_cpup((__le16*)&buffer[4]);
			n = 0;

			if (type == ITT_CAMERA) {
				if (buflen < 15 || buflen < 15 + buffer[14]) {
					uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
					       "interface %d INPUT_TERMINAL error\n", udev->devnum,
					       dev->intf->cur_altsetting->desc.bInterfaceNumber);
					return -EINVAL;
				}
				n = buffer[14];
			}

			term = kmalloc(sizeof *term + n, GFP_KERNEL);
			if (term == NULL)
				return -ENOMEM;
			memset(term, 0, sizeof *term + n);

			term->id = buffer[3];
			term->type = type;

			if (term->type == 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);
			}

			if (buffer[7] != 0)
				usb_string(udev, buffer[7], term->name, sizeof term->name);
			else if (term->type == ITT_CAMERA)
				sprintf(term->name, "Camera %u", buffer[3]);
			else
				sprintf(term->name, "Input %u", buffer[3]);

			list_add_tail(&term->list, &dev->terminals);
			break;

		case VC_OUTPUT_TERMINAL:
			if (buflen < 9) {
				uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
				       "interface %d OUTPUT_TERMINAL error\n", udev->devnum,
				       dev->intf->cur_altsetting->desc.bInterfaceNumber);
				return -EINVAL;
			}

			term = kmalloc(sizeof *term, GFP_KERNEL);
			if (term == NULL)
				return -ENOMEM;
			memset(term, 0, sizeof *term);

			term->id = buffer[3];
			term->type = le16_to_cpup((__le16*)&buffer[4]);
			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->terminals);
			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,
				       dev->intf->cur_altsetting->desc.bInterfaceNumber);
				return -EINVAL;
			}

			unit = kmalloc(sizeof *unit + p, GFP_KERNEL);
			if (unit == NULL)
				return -ENOMEM;
			memset(unit, 0, sizeof *unit + p);

			unit->id = buffer[3];
			unit->type = buffer[2];
			unit->selector.bNrInPins = buffer[5];
			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->units);
			break;

		case VC_PROCESSING_UNIT:
			n = buflen >= 8 ? buffer[7] : 0;

			if (buflen < 8 + n) {
				uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
				       "interface %d PROCESSING_UNIT error\n", udev->devnum,
				       dev->intf->cur_altsetting->desc.bInterfaceNumber);
				return -EINVAL;
			}

			unit = kmalloc(sizeof *unit + n, GFP_KERNEL);
			if (unit == NULL)
				return -ENOMEM;
			memset(unit, 0, sizeof *unit + n);

			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);
			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->units);
			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,
				       dev->intf->cur_altsetting->desc.bInterfaceNumber);
				return -EINVAL;
			}

			unit = kmalloc(sizeof *unit + p + n, GFP_KERNEL);
			if (unit == NULL)
				return -ENOMEM;
			memset(unit, 0, sizeof *unit + p + n);

			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->units);
			break;

		default:
			uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE "
				"descriptor (%u)\n", buffer[2]);
			break;
		}

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 (desc->bmAttributes == USB_ENDPOINT_XFER_INT &&
		    (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
		    	USB_DIR_IN &&
		    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 only a Processing Unit, optional Extension Units and an
 * Input Terminal.
 */
static int uvc_scan_chain(struct uvc_video_device *video)
{
	struct uvc_terminal *term;
	struct uvc_unit *unit;
	unsigned int ext = 0;
	int id;

	term = video->oterm;
	uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", term->id);
	id = term->output.bSourceID;
	while (1) {
		unit = uvc_unit_by_id(video->dev, id);
		if (unit == NULL)
			break;

		if (trace & UVC_TRACE_PROBE)
			printk(" <- Unit %d", unit->id);
		switch (unit->type) {
		case VC_EXTENSION_UNIT:
			if (unit->extension.bNrInPins != 1) {
				uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has"
					"more than 1 input pin.\n", id);
				return -1;
			}

			video->extension[ext++] = unit;
			id = unit->extension.baSourceID[0];
			break;

		case VC_PROCESSING_UNIT:
			if (video->processing != NULL) {
				uvc_trace(UVC_TRACE_DESCR, "Found multiple "
					"Processing Units in chain.\n");
				return -1;
			}

			video->processing = unit;
			id = unit->processing.bSourceID;
			break;

		case VC_SELECTOR_UNIT:
			uvc_trace(UVC_TRACE_DESCR, "Selector units are not "
				"supported yet.\n");
			return -1;
		}
	}

	term = uvc_terminal(video->dev, id);
	if (term == NULL) {
		uvc_trace(UVC_TRACE_DESCR, "Input terminal %d not found.\n", id);
		return -1;
	}

	if (trace & UVC_TRACE_PROBE)
		printk(" <- IT %d\n", term->id);
	video->iterm = term;

	/* 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.
 */
static int uvc_register_video(struct uvc_device *dev)
{
	struct video_device *vdev;
	struct list_head *p;
	int found = 0, ret;

	/* Check if the control interface matches the structure we expect. */
	list_for_each(p, &dev->terminals) {
		struct uvc_terminal *term;
		struct list_head *ps;

		term = list_entry(p, struct uvc_terminal, list);
		if (term->type != TT_STREAMING)
			continue;

		memset(&dev->video, 0, sizeof dev->video);
		dev->video.oterm = term;
		dev->video.dev = dev;
		if (uvc_scan_chain(&dev->video) < 0)
			continue;

		list_for_each(ps, &dev->streaming) {
			struct uvc_streaming *streaming;

			streaming = list_entry(ps, struct uvc_streaming, list);
			if (streaming->input.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");
		if (dev->udev->descriptor.idVendor == 0x05ac &&
		    dev->udev->descriptor.idProduct == 0x8300) {
			uvc_printk (KERN_ERR, "Possible an Apple iSight "
				    "without firmware loaded; please read "
				    "the documentation, load the firmware "
				    "and re-load the driver.");
		}
		return -1;
	}

	uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%d -> %d).\n",
		dev->video.iterm->id, 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;

	sprintf(vdev->name, "USB Video Class");
	/* 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->udev->dev;
	vdev->type = 0;
	vdev->type2 = 0;
	vdev->hardware = 0;
	vdev->minor = -1;
	vdev->fops = &uvc_fops;
	vdev->release = video_device_release;

	/* Set the driver data before calling video_register_device, otherwise
	 * uvc_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_open/uvc_release, and
 * unregister the video devices on disconnect when that reference count drops
 * to zero.
 */
static void uvc_delete(struct kref *kref)
{
	struct uvc_device *dev = container_of(kref, struct uvc_device, kref);
	struct list_head *p, *n;

	uvc_unregister_video(dev);
	usb_put_dev(dev->udev);

	list_for_each_safe(p, n, &dev->terminals) {
		struct uvc_terminal *terminal;
		terminal = list_entry(p, struct uvc_terminal, list);
		kfree(terminal);
	}

	list_for_each_safe(p, n, &dev->units) {
		struct uvc_unit *unit;
		unit = list_entry(p, struct uvc_unit, list);
		kfree(unit);
	}

	list_for_each_safe(p, n, &dev->streaming) {
		struct uvc_streaming *streaming;
		streaming = list_entry(p, struct uvc_streaming, list);
		kfree(streaming->format);
		kfree(streaming->input.bmaControls);
		kfree(streaming);
	}

	if (dev->int_urb) {
		usb_kill_urb(dev->int_urb);
		usb_free_urb(dev->int_urb);
	}

	kfree(dev);
}

static int uvc_init_status(struct uvc_device *dev)
{
	struct usb_endpoint_descriptor *desc = &dev->int_ep->desc;
	unsigned int pipe;

	dev->int_urb = usb_alloc_urb(0, GFP_KERNEL);
	if (dev->int_urb == NULL)
		return -ENOMEM;

	pipe = usb_rcvintpipe(dev->udev, desc->bEndpointAddress);

	usb_fill_int_urb(dev->int_urb, dev->udev, pipe,
		dev->status, sizeof dev->status, uvc_status_complete,
		dev, desc->bInterval);

	return usb_submit_urb(dev->int_urb, GFP_KERNEL);
}

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);

	if ((dev = kmalloc(sizeof *dev, GFP_KERNEL)) == NULL)
		return -ENOMEM;

	/* Allocate memory for the device and initialize it */
	memset(dev, 0, sizeof *dev);
	INIT_LIST_HEAD(&dev->terminals);
	INIT_LIST_HEAD(&dev->units);
	INIT_LIST_HEAD(&dev->streaming);
	kref_init(&dev->kref);

	dev->udev = usb_get_dev(udev);
	dev->intf = intf;
	dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;

	/* 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>",
		udev->descriptor.idVendor, udev->descriptor.idProduct);

	/* Register the video devices */
	uvc_register_video(dev);

	/* Save our data pointer in the interface data */
	usb_set_intfdata(intf, dev);

	/* Initialize the interrupt URB */
	if (dev->int_ep != NULL && (ret = uvc_init_status(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);

	if (dev == (void*)-1)
		return;

	/* 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);

	/* uvc_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_open() received the pointer to
	 * the device (video_devdata) but before it got the chance to increase
	 * the reference count (kref_get). An alternative (used by the
	 * usb-skeleton driver) would have been to use the big kernel lock
	 * instead of a driver-specific semaphore (open calls on char devices
	 * are protected by the BKL), but that approach is not recommanded for
	 * new code.
	 */
	down(&dev_sem);

	dev->state |= UVC_DEV_DISCONNECTED;
	kref_put(&dev->kref, uvc_delete);

	up(&dev_sem);
}

/* ------------------------------------------------------------------------
 * 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[] = {
	/* 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 */
	{ .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 },
	/* Apple iSight (built-in in Macintels) */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE,
	  .idVendor		= 0x05ac,
	  .idProduct		= 0x8501 },
	/* same, but without firmware loaded (will give useful warning)
	 * when the firmware is not loaded in the pre-instal step). */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x05ac,
	  .idProduct		= 0x8300,
	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
	  .bInterfaceSubClass	= 0xff,
	  .bInterfaceProtocol	= 0xff },
	/* Generic USB Video Class */
	{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
	{}
};

MODULE_DEVICE_TABLE(usb, uvc_ids);

static struct usb_driver uvc_driver = {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16)
	.owner		= THIS_MODULE,
#endif
	.name		= "uvcvideo",
	.probe		= uvc_probe,
	.disconnect	= uvc_disconnect,
	.id_table	= uvc_ids,
};

static int __init uvc_init(void)
{
	int result;
	result = usb_register(&uvc_driver);
	if (result == 0)
		printk(KERN_INFO DRIVER_DESC " (v" DRIVER_VERSION ")\n");
	return result;
}

static void __exit uvc_cleanup(void)
{
	usb_deregister(&uvc_driver);
}

module_init(uvc_init);
module_exit(uvc_cleanup);

module_param(trace, uint, S_IRUGO|S_IWUSR);

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

