OTG与gadget学习(四)

     现在可以识别出来了,太高兴了。离成功又近了一步。

      EP0_BUFSIZE 这个和struct usb_descriptor_header *hw_function[] 比较关键,EP0_BUFSIZE定义了ep0端点的Buf的大小,要能存放下最大的返回数据,通过之前bus hound查看与分析,真实摄像头返回最大是933字节,返回的内容包含了hw_function。但是目前,若hw_function包含的太多,就会一直“on state DATA_STATE_XMIT”,导致后面枚举无法进行下去,一直reset,最后变成unknown device。先减少内容,以后再想想该怎么做。

     先把目前的代码放这。

mygadget.c:

<pre name="code" class="cpp">#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/video.h>
#include <linux/usb/composite.h>
#include <linux/freezer.h>
#include <linux/kthread.h>
#include <linux/types.h>

#include <asm/uaccess.h>

#include "composite.c"
#include "usbstring.c"
#include "config.c"
#include "epautoconf.c"

/* real usb cam */
int use_hw;

struct vc_intf_header_desc {
	__u8	bLength;
	__u8	bDescriptorType;
	__u8	bDescriptorSubType;
	__le16	bcdUVC;
	__le16	wTotalLength;
	__le32	dwClockFrequency;
	__u8	bInCollection;
	__u8	baInterfaceNr;
} __attribute__ ((packed));

struct vc_intf_it_desc {
	__u8 	bLength;
	__u8 	bDescriptorType;
	__u8 	bDescriptorSubType;
	__u8 	bTerminalID;
	__le16 	wTerminalType;
	__u8 	bAssocTerminal;
	__u8 	iTerminal;
	__le16 	wObjectiveFocalLengthMin;
	__le16 	wObjectiveFocalLengthMax;
	__le16 	wOcularFocalLength;
	__u8 	bControlSize;
	__le32 	bmControls:24;
}__attribute__ ((packed));

struct vc_intf_eu_desc {
	__u8 bLength;
	__u8 bDescriptorType;
	__u8 bDescriptorSubType;
	__u8 bUnitID;
	__u8 guidExtensionCode[16];
	__u8 bNumControls;
	__u8 bNrPins;
	__u8 baSourceID;
	__u8 bControlSize;
	__u8 bmControls;
	__u8 iExtension;
}__attribute__ ((packed));

struct vc_intf_pu_desc {
	__u8 	bLength;
	__u8 	bDescriptorType;
	__u8 	bDescriptorSubType;
	__u8 	bUnitID;
	__u8 	baSourceID;
	__le16 	wMaxMultiplier;
	__u8 	bControlSize;
	__le16 	bmControls;
	__u8 	iProcessing;
}__attribute__ ((packed));

struct vc_intf_ot_desc {
	__u8 	bLength;
	__u8 	bDescriptorType;
	__u8 	bDescriptorSubType;
	__u8 	bTerminalID;
	__le16 	wTerminalType;
	__u8	bAssocTerminal;
	__u8 	bSourceID;
	__u8 	iTerminal;
}__attribute__ ((packed));

struct vc_interface {
	struct vc_intf_header_desc header_desc; //13byte
	struct vc_intf_it_desc it_desc;  //18byte
	struct vc_intf_eu_desc eu_desc;  //26byte
	struct vc_intf_pu_desc pu_desc;  //11byte
	struct vc_intf_ot_desc ot_desc;  //9byte
}__attribute__ ((packed));

struct usb_ep_extra {
	__u8 data[5];
}__attribute__ ((packed));

struct hardware_device_vc_interface {
	struct usb_interface_descriptor	desc;
	struct vc_interface vc_intf;
	struct usb_endpoint_descriptor ep_desc;
	struct usb_ep_extra ep_extra;
};

struct vs_intf_header_desc {
	__u8 	bLength;
	__u8 	bDescriptorType;
	__u8 	bDescriptorSubType;
	__u8 	bNumFormats;
	__le16 	wTotalLength;
	__u8 	bEndPointAddress;
	__u8 	bmInfo;
	__u8 	bTerminalLink;
	__u8 	bStillCaptureMethod;
	__u8 	bTriggerSupport;
	__u8 	bTriggerUsage;
	__u8 	bControlSize;
	__u8 	bmaControls[2];
}__attribute__ ((packed));

struct vs_intf_format_mjpeg_desc {
	__u8 bLength;
	__u8 bDescriptorType;
	__u8 bDescriptorSubtype;
	__u8 bFormatIndex;
	__u8 bNumFrameDescriptors;
	__u8 bFlags;
	__u8 bDefaultFrameIndex;
	__u8 bAspectRatioX;
	__u8 bAspectRatioY;
	__le16 bmInterlaceFlags;
}__attribute__ ((packed));

struct vs_intf_frame_desc {
	__u8 	bLength;
	__u8 	bDescriptorType;
	__u8 	bDescriptorSubtype;
	__u8 	bFrameIndex;
	__u8 	bmCapabilities;
	__le16 	wWidth;
	__le16 	wHeight;
	__le32 	dwMinBitRate;
	__le32 	dwMaxBitRate;
	__le32 	dwMaxVideoFrameBufferSize;
	__le32	dwDefaultFrameInterval;
	__u8 	bFrameIntervalType;
	__le32	dwFrameInterval;
}__attribute__ ((packed));

struct ImageSizePattern {
	__le16 wWidth;
	__le16 wHeight;
}__attribute__ ((packed));

struct vs_intf_still_desc {
	__u8 bLength;
	__u8 bDescriptorType;
	__u8 bDescriptorSubtype;
	__u8 bEndpointAddress;
	__u8 bNumImageSizePatterns;
	/* bNumImageSizePatterns=8 */
	struct ImageSizePattern bImageSize[8];
	__u8 bNumCompressionPatterns;
}__attribute__ ((packed));

struct vs_intf_colorFormat_desc {
	__u8 bLength;
	__u8 bDescriptorType;
	__u8 bDescriptorSubtype;
	__u8 bColorPrimaries;
	__u8 bTransferCharacteristics;
	__u8 bMatrixCoefficients;
}__attribute__ ((packed));

struct vs_intf_format_uncmps_desc {
	__u8 bLength;
	__u8 bDescriptorType;
	__u8 bDescriptorSubtype;
	__u8 bFormatIndex;
	__u8 bNumFrameDescriptors;
	__u8 guidFormat[16];
	__u8 bBitsPerPixel;
	__u8 bDefaultFrameIndex;
	__u8 bAspectRatioX;
	__u8 bAspectRatioY;
	__le16 bmInterlaceFlags;
}__attribute__ ((packed));

struct vs_interface {
	struct vs_intf_header_desc header_desc; //15byte
	
	struct vs_intf_format_mjpeg_desc mjpg_fmt_desc;  //11byte
	/* fmt_mjpg_desc.bNumFrameDescriptors=9 */
	struct vs_intf_frame_desc mjpg_frame_desc[9];  //30byte
	struct vs_intf_still_desc mjpg_still_desc;
	struct vs_intf_colorFormat_desc mjpg_clrFmt_desc;
	
	struct vs_intf_format_uncmps_desc uncmps_fmt_desc;
	/* fmt_uncmps_desc.bNumFrameDescriptors=9 */
	struct vs_intf_frame_desc uncmps_frame_desc[9];
	struct vs_intf_still_desc uncmps_still_desc;
	struct vs_intf_colorFormat_desc uncmps_clrFmt_desc;
}__attribute__ ((packed));

struct vs_interface_altsetting {
	struct usb_interface_descriptor	desc;
	struct usb_endpoint_descriptor ep_desc;
};

struct hardware_device_vs_interface {
	struct usb_interface_descriptor	desc;
	struct vs_interface vs_intf;
	struct vs_interface_altsetting altsetting[8]; 
};

struct hardware_device_config {
	struct usb_config_descriptor desc;
	struct usb_interface_assoc_descriptor intf_assoc_desc;
	struct hardware_device_vc_interface vc_interf;
	struct hardware_device_vs_interface vs_interf;
}; 

struct hardware_device {
	struct usb_device_descriptor desc;
	struct usb_host_endpoint ep0;
	struct hardware_device_config config;
};

struct hardware_device hw_dev;

struct usb_string hw_strings[5];

/* ---------- */

#define DEBUG

#undef DBG
#ifdef DEBUG
 #define  DBG(x...) printk(x)
#else
 #define  DBG(x...) do { } while (0)
#endif

static const char mygadget_string_product[] = "My Gadget";

/*
     ep0's buffer's size,
     DT_CONFIG, need transfer 933bytes
     so it must be setted more than 933
     but how much to set
*/
#define EP0_BUFSIZE	950
#define DELAYED_STATUS	(EP0_BUFSIZE + 299)	/* An impossibly large value */
/* Bulk-only class specific requests */
#define USB_BULK_RESET_REQUEST		0xff
#define USB_BULK_GET_MAX_LUN_REQUEST	0xfe

/* atomic_bitflags */
#define REGISTERED			0
#define IGNORE_BULK_OUT		1
#define SUSPENDED			2

typedef enum {
	/* This one isn't used anywhere */
	MYGADGET_STATE_COMMAND_PHASE = -10,
	MYGADGET_STATE_DATA_PHASE,
	MYGADGET_STATE_STATUS_PHASE,

	MYGADGET_STATE_IDLE = 0,
	MYGADGET_STATE_ABORT_BULK_OUT,
	MYGADGET_STATE_RESET,
	MYGADGET_STATE_INTERFACE_CHANGE,
	MYGADGET_STATE_CONFIG_CHANGE,
	MYGADGET_STATE_DISCONNECT,
	MYGADGET_STATE_EXIT,
	MYGADGET_STATE_TERMINATED
} MYGADGET_STATE;


struct mygadget_dev {
	struct usb_gadget *gadget;

	struct usb_request *ep0req;	// For control responses
	unsigned int ep0_req_tag;
	const char *ep0req_name;

	spinlock_t lock;
	struct task_struct *thread_task;
	struct completion thread_notifier;
	unsigned int exception_req_tag;

	MYGADGET_STATE state;

	unsigned long atomic_bitflags;

	unsigned char config, new_config;

	struct usb_request *request;
	struct usb_ep *bulk_in;
	struct usb_ep *bulk_out;

	unsigned int running : 1;
	unsigned int bulk_in_enabled : 1;
	unsigned int bulk_out_enabled : 1;
	unsigned int intr_in_enabled : 1;
	unsigned int phase_error : 1;
	unsigned int short_packet_received : 1;
	unsigned int bad_lun_okay : 1;
};

static struct mygadget_dev *gMygadget;

static struct usb_endpoint_descriptor mygadget_fs_bulk_out_desc = {
	.bLength 			= USB_DT_ENDPOINT_SIZE,
	.bDescriptorType 	= USB_DT_ENDPOINT,

	.bEndpointAddress 	= USB_DIR_OUT,
	.bmAttributes 		= USB_ENDPOINT_XFER_BULK,
	/* wMaxPacketSize set by autoconfiguration */
};

static struct usb_endpoint_descriptor mygadget_hs_bulk_out_desc = {
	.bLength 			= USB_DT_ENDPOINT_SIZE,
	.bDescriptorType 	= USB_DT_ENDPOINT,

	.bEndpointAddress	= USB_DIR_OUT,
	.bmAttributes 		= USB_ENDPOINT_XFER_BULK,
	.wMaxPacketSize 	= 512,
};

static struct usb_otg_descriptor mygadget_otg_desc = {
	.bLength =		sizeof mygadget_otg_desc,
	.bDescriptorType =	USB_DT_OTG,

	.bmAttributes =		USB_OTG_SRP,
};

static struct usb_descriptor_header *hw_function[] = {
	(struct usb_descriptor_header *) &mygadget_otg_desc,
	(struct usb_descriptor_header *) &hw_dev.config.intf_assoc_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vc_interf.desc,
	(struct usb_descriptor_header *) &hw_dev.config.vc_interf.vc_intf.header_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vc_interf.vc_intf.it_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vc_interf.vc_intf.eu_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vc_interf.vc_intf.pu_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vc_interf.vc_intf.ot_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vc_interf.ep_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vc_interf.ep_extra,	
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.header_desc,
#if 0
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.mjpg_fmt_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.mjpg_frame_desc[0],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.mjpg_frame_desc[1],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.mjpg_frame_desc[2],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.mjpg_frame_desc[3],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.mjpg_frame_desc[4],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.mjpg_frame_desc[5],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.mjpg_frame_desc[6],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.mjpg_frame_desc[7],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.mjpg_frame_desc[8],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.mjpg_still_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.mjpg_clrFmt_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.uncmps_fmt_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.uncmps_frame_desc[0],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.uncmps_frame_desc[1],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.uncmps_frame_desc[2],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.uncmps_frame_desc[3],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.uncmps_frame_desc[4],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.uncmps_frame_desc[5],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.uncmps_frame_desc[6],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.uncmps_frame_desc[7],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.uncmps_frame_desc[8],
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.uncmps_still_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.vs_intf.uncmps_clrFmt_desc,

	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.altsetting[0].desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.altsetting[0].ep_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.altsetting[1].desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.altsetting[1].ep_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.altsetting[2].desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.altsetting[2].ep_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.altsetting[3].desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.altsetting[3].ep_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.altsetting[4].desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.altsetting[4].ep_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.altsetting[5].desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.altsetting[5].ep_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.altsetting[6].desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.altsetting[6].ep_desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.altsetting[7].desc,
	(struct usb_descriptor_header *) &hw_dev.config.vs_interf.altsetting[7].ep_desc,
#endif
	NULL,
};

static struct usb_qualifier_descriptor mygadget_dev_qualifier = {
	.bLength 			= sizeof(mygadget_dev_qualifier),
	.bDescriptorType 	= USB_DT_DEVICE_QUALIFIER,

	.bcdUSB 			= cpu_to_le16(0x0200),
	.bDeviceClass 		= USB_CLASS_MISC,

	.bNumConfigurations = 1,
};

static void mygadget_raise_exception(struct mygadget_dev *mydev, MYGADGET_STATE state)
{
	unsigned long		flags;

	/* Do nothing if a higher-priority exception is already in progress.
	 * If a lower-or-equal priority exception is in progress, preempt it
	 * and notify the main thread by sending it a signal. */
	spin_lock_irqsave(&mydev->lock, flags);
	if (mydev->state <= state) {
		mydev->exception_req_tag = mydev->ep0_req_tag;
		mydev->state = state;
		if (mydev->thread_task)
			send_sig_info(SIGUSR1, SEND_SIG_FORCED,
					mydev->thread_task);
	}
	spin_unlock_irqrestore(&mydev->lock, flags);
}

static void __exit mygadget_unbind(struct usb_gadget *gadget)
{
	struct mygadget_dev	*mydev = get_gadget_data(gadget);
	struct usb_request	*req = mydev->ep0req;
	
	printk("%s\n", __func__);
	
	clear_bit(REGISTERED, &mydev->atomic_bitflags);

	/* If the thread isn't already dead, tell it to exit now */
	if (mydev->state != MYGADGET_STATE_TERMINATED) {
		mygadget_raise_exception(mydev, MYGADGET_STATE_EXIT);

		wait_for_completion(&mydev->thread_notifier);

		/* The cleanup routine waits for this completion also */
		complete(&mydev->thread_notifier);
	}

	/* Free the request and buffer for endpoint 0 */
	if(req) {
		kfree(req->buf);
		usb_ep_free_request(gadget->ep0, req);
	}
}

static void mygadget_disconnect(struct usb_gadget *gadget)
{
	struct mygadget_dev	*mydev = get_gadget_data(gadget);

	DBG("%s: disconnect or port reset\n", __func__);
	mygadget_raise_exception(mydev, MYGADGET_STATE_DISCONNECT);
}

static int mygadget_class_setup_req(struct mygadget_dev *dev,
		const struct usb_ctrlrequest *ctrl)
{
	int value = -EOPNOTSUPP;
	u16	w_index = le16_to_cpu(ctrl->wIndex);
	u16 w_value = le16_to_cpu(ctrl->wValue);
	u16	w_length = le16_to_cpu(ctrl->wLength);

	if (!dev->config)
		return value;

	/* Handle Bulk-only class-specific requests */
	switch (ctrl->bRequest) {

	case USB_BULK_RESET_REQUEST:
		DBG("%s: USB_BULK_RESET_REQUEST\n", __func__);
		if (ctrl->bRequestType != (USB_DIR_OUT |
				USB_TYPE_CLASS | USB_RECIP_INTERFACE))
			break;
		if (w_index != 0 || w_value != 0) {
			value = -EDOM;
			break;
		}

		/* Raise an exception to stop the current operation
		 * and reinitialize our state. */
		DBG("%s: bulk reset request\n", __func__);
		mygadget_raise_exception(dev, MYGADGET_STATE_RESET);
		value = DELAYED_STATUS;
		break;

	case USB_BULK_GET_MAX_LUN_REQUEST:
		DBG("%s: USB_BULK_GET_MAX_LUN_REQUEST\n", __func__);
		if (ctrl->bRequestType != (USB_DIR_IN |
				USB_TYPE_CLASS | USB_RECIP_INTERFACE))
			break;
		if (w_index != 0 || w_value != 0) {
			value = -EDOM;
			break;
		}
		VDBG(dev, "get max LUN\n");
		value = 1;
		break;
	}

	if (value == -EOPNOTSUPP)
		VDBG(dev,
			"unknown class-specific control req "
			"%02x.%02x v%04x i%04x l%u\n",
			ctrl->bRequestType, ctrl->bRequest,
			le16_to_cpu(ctrl->wValue), w_index, w_length);
	
	return value;
}

static struct usb_gadget_strings mygadget_stringtab = {
	.language = 0x0409,	/* en-us */
	.strings = hw_strings,
};

/*
 * Config descriptors must agree with the code that sets configurations
 * and with code managing interfaces and their altsettings.  They must
 * also handle different speeds and other-speed requests.
 */
static int mygadget_pop_config_buf(struct usb_gadget *gadget,
		u8 *buf, u8 type, unsigned index)
{
	enum usb_device_speed speed = gadget->speed;
	int len;
	const struct usb_descriptor_header	**function;

	if (index > 0)
		return -EINVAL;

	if (gadget_is_dualspeed(gadget) && type == USB_DT_OTHER_SPEED_CONFIG)
		speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed;

	if(use_hw) {
		DBG("%s: use hw func\n", __func__);
		/*
		function = gadget_is_dualspeed(gadget) && speed == USB_SPEED_HIGH
			? (const struct usb_descriptor_header **)hw_hs_function
			: (const struct usb_descriptor_header **)hw_fs_function;
		*/
		function = (const struct usb_descriptor_header **)hw_function;
	} else {
		DBG("%s: use mygadget func\n", __func__);
	}

	/* for now, don't add otg func */
	if (!gadget_is_otg(gadget))
		function++;
	
	if(use_hw) {
		DBG("%s: use hw config\n", __func__);
		if(*function)
			DBG("%s: (*function)->bLength=%u\n", __func__, (*function)->bLength);
		else
			DBG("%s: *function is null\n", __func__);
		
		len = usb_gadget_config_buf(&hw_dev.config.desc, buf, EP0_BUFSIZE, function);
	} else {
		DBG("%s: use mygadget config\n", __func__);
	}
	
	((struct usb_config_descriptor *) buf)->bDescriptorType = type;
	
	return len;
}


/* Ep0 standard request handlers.  These always run in_irq. */
static int mygadget_standard_setup_req(struct mygadget_dev *dev,
		const struct usb_ctrlrequest *ctrl)
{
	struct usb_request	*req = dev->ep0req;
	int	value = -EOPNOTSUPP;
	u16	w_index = le16_to_cpu(ctrl->wIndex);
	u16	w_value = le16_to_cpu(ctrl->wValue);

	/* Usually this just stores reply data in the pre-allocated ep0 buffer,
	 * but config change events will also reconfigure hardware. */
	switch (ctrl->bRequest) {
	case USB_REQ_GET_DESCRIPTOR:
		DBG("//%s\\\\: USB_REQ_GET_DESCRIPTOR\n", __func__);
		if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD |
				USB_RECIP_DEVICE))
			break;
		switch (w_value >> 8) {
		case USB_DT_DEVICE:
			DBG("//%s\\\\: USB_DT_DEVICE\n", __func__);
			VDBG(dev, "get device descriptor\n");

			if(use_hw) {
				DBG("//%s\\\\: use hw_dev\n", __func__);
				value = sizeof(hw_dev.desc);
				memcpy(req->buf, &hw_dev.desc, value);
			} else {
				DBG("//%s\\\\: use mygadget\n", __func__);
			}
			break;
		case USB_DT_DEVICE_QUALIFIER:
			DBG("//%s\\\\: USB_DT_DEVICE_QUALIFIER\n", __func__);
			VDBG(dev, "get device qualifier\n");
			if (!gadget_is_dualspeed(dev->gadget))
				break;
			value = sizeof(mygadget_dev_qualifier);
			memcpy(req->buf, &mygadget_dev_qualifier, value);
			break;

		case USB_DT_OTHER_SPEED_CONFIG:
			DBG("//%s\\\\: USB_DT_OTHER_SPEED_CONFIG\n", __func__);
			VDBG(dev, "get other-speed config descriptor\n");
			if (!gadget_is_dualspeed(dev->gadget))
				break;
			goto get_config;
		case USB_DT_CONFIG:
			DBG("//%s\\\\: USB_DT_CONFIG\n", __func__);
			VDBG(dev, "get configuration descriptor\n");
get_config:
			value = mygadget_pop_config_buf(dev->gadget,
					req->buf,
					w_value >> 8,
					w_value & 0xff);
			break;

		case USB_DT_STRING:
			DBG("//%s\\\\: USB_DT_STRING\n", __func__);
			VDBG(dev, "get string descriptor\n");

			/* wIndex == language code */
			value = usb_gadget_get_string(&mygadget_stringtab,
					w_value & 0xff, req->buf);
			break;
		}
		break;

	/* One config, two speeds */
	case USB_REQ_SET_CONFIGURATION:
		DBG("//%s\\\\: USB_REQ_SET_CONFIGURATION\n", __func__);
		if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD |
				USB_RECIP_DEVICE))
			break;
		VDBG(dev, "set configuration\n");
		if (w_value == 1 || w_value == 0) {
			dev->new_config = w_value;

			/* Raise an exception to wipe out previous transaction
			 * state (queued bufs, etc) and set the new config. */
			mygadget_raise_exception(dev, MYGADGET_STATE_CONFIG_CHANGE);
			value = DELAYED_STATUS;
		}
		break;
	case USB_REQ_GET_CONFIGURATION:
		DBG("//%s\\\\: USB_REQ_GET_CONFIGURATION\n", __func__);
		if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD |
				USB_RECIP_DEVICE))
			break;
		VDBG(dev, "get configuration\n");
		*(u8 *) req->buf = dev->config;
		value = 1;
		break;

	case USB_REQ_SET_INTERFACE:
		DBG("//%s\\\\: USB_REQ_SET_INTERFACE\n", __func__);
		if (ctrl->bRequestType != (USB_DIR_OUT| USB_TYPE_STANDARD |
				USB_RECIP_INTERFACE))
			break;
		if (dev->config && w_index == 0) {

			/* Raise an exception to wipe out previous transaction
			 * state (queued bufs, etc) and install the new
			 * interface altsetting. */
			mygadget_raise_exception(dev, MYGADGET_STATE_INTERFACE_CHANGE);
			value = DELAYED_STATUS;
		}
		break;
	case USB_REQ_GET_INTERFACE:
		DBG("//%s\\\\: USB_REQ_GET_INTERFACE\n", __func__);
		if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD |
				USB_RECIP_INTERFACE))
			break;
		if (!dev->config)
			break;
		if (w_index != 0) {
			value = -EDOM;
			break;
		}
		VDBG(dev, "get interface\n");
		*(u8 *) req->buf = 0;
		value = 1;
		break;

	default:
		DBG("//%s\\\\: default\n", __func__);
		VDBG(dev,
			"unknown control req %02x.%02x v%04x i%04x l%u\n",
			ctrl->bRequestType, ctrl->bRequest,
			w_value, w_index, le16_to_cpu(ctrl->wLength));
	}

	return value;
}

static int mygadget_ep0_queue(struct mygadget_dev *dev)
{
	int	rc;

	rc = usb_ep_queue(dev->gadget->ep0, dev->ep0req, GFP_ATOMIC);
	if (rc != 0 && rc != -ESHUTDOWN) {

		/* We can't do much more than wait for a reset */
		WARNING(dev, "error in submission: %s --> %d\n",
				dev->gadget->ep0->name, rc);
	}
	return rc;
}

static int mygadget_setup(struct usb_gadget *gadget,
		const struct usb_ctrlrequest *ctrl)
{
	struct mygadget_dev	*mydev = get_gadget_data(gadget);
	int ret = 0;
	int	w_length = le16_to_cpu(ctrl->wLength);

	printk("%s: ctrl->wLength=%d\n", __func__, w_length);

	++mydev->ep0_req_tag;		// Record arrival of a new request
	mydev->ep0req->context = NULL;
	mydev->ep0req->length = 0;
	
	if(sizeof(*ctrl) < 512) {
		DBG("%s: ep0-setup, length %u:\n", __func__, sizeof(*ctrl));
		print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,
				   16, 1, (u8*)ctrl, sizeof(*ctrl), 0);
	}

	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS)
		ret = mygadget_class_setup_req(mydev, ctrl);
	else
		ret = mygadget_standard_setup_req(mydev, ctrl);
	
	/* Respond with data/status or defer until later? */
	if (ret >= 0 && ret != DELAYED_STATUS) {
		ret = min(ret, w_length);
		mydev->ep0req->length = ret;
		mydev->ep0req->zero = ret < w_length;
		mydev->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ?
				"ep0-in" : "ep0-out");
		ret = mygadget_ep0_queue(mydev);
	}

	return ret;
}

static void mygadget_suspend(struct usb_gadget *gadget)
{
	struct mygadget_dev	*mydev = get_gadget_data(gadget);

	printk("%s\n", __func__);
	set_bit(SUSPENDED, &mydev->atomic_bitflags);
}

static void mygadget_resume(struct usb_gadget *gadget)
{
	struct mygadget_dev	*mydev = get_gadget_data(gadget);

	printk("%s\n", __func__);
	set_bit(SUSPENDED, &mydev->atomic_bitflags);
}

static struct usb_gadget_driver	mygadget_driver = {
#ifdef CONFIG_USB_GADGET_DUALSPEED
	.speed		= USB_SPEED_HIGH,
#else
	.speed		= USB_SPEED_FULL,
#endif
	.function	= (char *) mygadget_string_product,
	.unbind		= mygadget_unbind,
	.disconnect	= mygadget_disconnect,
	.setup		= mygadget_setup,
	.suspend	= mygadget_suspend,
	.resume		= mygadget_resume,

	.driver		= {
		.name		= "MyGadget",
		.owner		= THIS_MODULE,
		// .release = ...
		// .suspend = ...
		// .resume = ...
	},
};

typedef void (*mygadget_routine_t)(struct mygadget_dev *);

static void mygadget_ep0_complete(struct usb_ep *ep, struct usb_request *req)
{
	struct mygadget_dev *mydev = ep->driver_data;

	DBG("%s\n", __func__);
	
	if(req->actual > 0 && req->actual < EP0_BUFSIZE) {
		DBG("actual: %u, len: %u\n", req->actual, req->length);
		print_hex_dump(KERN_NOTICE, "", DUMP_PREFIX_OFFSET,
						16, 1, req->buf, req->actual, 0);
	}
	
	if(req->status || req->actual != req->length)
		DBG("%s --> %d, %u/%u\n", __func__, req->status, req->actual, req->length);
	
	if(req->status == -ECONNRESET)		// Request was cancelled
		usb_ep_fifo_flush(ep);

	/* call the func that req->context point */
	if(req->status == 0 && req->context)
		((mygadget_routine_t) (req->context))(mydev);
}

/* Maxpacket and other transfer characteristics vary by speed. */
static struct usb_endpoint_descriptor *
mygadget_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
		struct usb_endpoint_descriptor *hs)
{
	if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
		return hs;
	return fs;
}

static int mygadget_enable_endpoint(struct mygadget_dev *mydev, 
	struct usb_ep *ep, const struct usb_endpoint_descriptor *d)
{
	int	rc;

	ep->driver_data = mydev;
	rc = usb_ep_enable(ep, d);
	if(rc) {
		DBG("%s: can't enable %s, result %d\n", __func__, ep->name, rc);
	}
	
	return rc;
}

/*
 * Reset interface setting and re-init endpoint state (toggle etc).
 * Call with altsetting < 0 to disable the interface.  The only other
 * available altsetting is 0, which enables the interface.
 */
static int mygadget_do_set_interface(struct mygadget_dev *mydev, int altsetting)
{
	int	rc = 0;
	//int	i;
	const struct usb_endpoint_descriptor *d;

	if (mydev->running)
		DBG("%s: reset interface\n", __func__);

reset:
	/* Deallocate the requests */
	/*
	for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
		struct fsg_buffhd *bh = &mydev->buffhds[i];

		if (bh->inreq) {
			usb_ep_free_request(mydev->bulk_in, bh->inreq);
			bh->inreq = NULL;
		}
		if (bh->outreq) {
			usb_ep_free_request(mydev->bulk_out, bh->outreq);
			bh->outreq = NULL;
		}
	}
	*/

	/* Disable the endpoints */
	if (mydev->bulk_in_enabled) {
		usb_ep_disable(mydev->bulk_in);
		mydev->bulk_in_enabled = 0;
	}
	if (mydev->bulk_out_enabled) {
		usb_ep_disable(mydev->bulk_out);
		mydev->bulk_out_enabled = 0;
	}

	mydev->running = 0;
	if (altsetting < 0 || rc != 0)
		return rc;

	DBG("set interface %d\n", altsetting);

	/* Enable the endpoints */
	d = mygadget_ep_desc(mydev->gadget,
			&hw_dev.config.vc_interf.ep_desc, &hw_dev.config.vc_interf.ep_desc);
	if ((rc = mygadget_enable_endpoint(mydev, mydev->bulk_in, d)) != 0)
		goto reset;
	mydev->bulk_in_enabled = 1;

	d = mygadget_ep_desc(mydev->gadget,
			&mygadget_fs_bulk_out_desc, &mygadget_hs_bulk_out_desc);
	if ((rc = mygadget_enable_endpoint(mydev, mydev->bulk_out, d)) != 0)
		goto reset;
	mydev->bulk_out_enabled = 1;
	//mydev->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize);
	clear_bit(IGNORE_BULK_OUT, &mydev->atomic_bitflags);

	/* Allocate the requests */
	/*
	for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
		struct fsg_buffhd	*bh = &mydev->buffhds[i];

		if ((rc = alloc_request(mydev, mydev->bulk_in, &bh->inreq)) != 0)
			goto reset;
		if ((rc = alloc_request(mydev, mydev->bulk_out, &bh->outreq)) != 0)
			goto reset;
		bh->inreq->buf = bh->outreq->buf = bh->buf;
		bh->inreq->context = bh->outreq->context = bh;
		bh->inreq->complete = bulk_in_complete;
		bh->outreq->complete = bulk_out_complete;
	}
	*/
	mydev->running = 1;

	return rc;
}


/*
 * Change our operational configuration.  This code must agree with the code
 * that returns config descriptors, and with interface altsetting code.
 *
 * It's also responsible for power management interactions.  Some
 * configurations might not work with our current power sources.
 * For now we just assume the gadget is always self-powered.
 */
static int mygadget_do_set_config(struct mygadget_dev *mydev, unsigned char config)
{
	int	rc = 0;

	/* Disable the single interface */
	if(mydev->config != 0) {
		DBG("%s: reset config\n", __func__);
		mydev->config = 0;
		rc = mygadget_do_set_interface(mydev, -1);
	}

	/* Enable the interface */
	if(config != 0) {
		mydev->config = config;
		if ((rc = mygadget_do_set_interface(mydev, 0)) != 0)
			mydev->config = 0;	// Reset on errors
		else {
			char *speed;

			switch (mydev->gadget->speed) {
			case USB_SPEED_LOW:	speed = "low";	break;
			case USB_SPEED_FULL:	speed = "full";	break;
			case USB_SPEED_HIGH:	speed = "high";	break;
			default: 		speed = "?";	break;
			}
			DBG("%s: %s speed config #%d\n", __func__, speed, mydev->config);
		}
	}
	return rc;
}

static int mygadget_set_halt(struct mygadget_dev *dev, struct usb_ep *ep)
{
	const char	*name;

	if (ep == dev->bulk_in)
		name = "bulk-in";
	else if (ep == dev->bulk_out)
		name = "bulk-out";
	else
		name = ep->name;
	DBG("[%s]: %s set halt\n", __func__, name);
	return usb_ep_set_halt(ep);
}

static void mygadget_handle_exception(struct mygadget_dev *dev)
{
	int sig;
	siginfo_t info;
	MYGADGET_STATE old_state;
	unsigned int exception_req_tag;
	unsigned char new_config;
	int rc = 0;

	/* Clear the existing signals.  Anything but SIGUSR1 is converted
	 * into a high-priority EXIT exception. */
	while(1) {
		sig = dequeue_signal_lock(current, ¤t->blocked, &info);
		if(!sig)
			break;
		
		if(sig != SIGUSR1) {
			if(dev->state < MYGADGET_STATE_EXIT)
				DBG("Main thread exiting on signal\n");

			mygadget_raise_exception(dev, MYGADGET_STATE_EXIT);
		}
	}

	/* Cancel all the pending transfers */
	/*
	for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
		bh = &dev->buffhds[i];
		if (bh->inreq_busy)
			usb_ep_dequeue(dev->bulk_in, bh->inreq);
		if (bh->outreq_busy)
			usb_ep_dequeue(dev->bulk_out, bh->outreq);
	}
	*/
	/* Wait until everything is idle */
	/*
	while(1) {
		num_active = dev->intreq_busy;
		for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
			bh = &dev->buffhds[i];
			num_active += bh->inreq_busy + bh->outreq_busy;
		}
		if (num_active == 0)
			break;
		if (sleep_thread(dev))
			return;
	}
	*/
	/* Clear out the controller's fifos */
	if (dev->bulk_in_enabled)
		usb_ep_fifo_flush(dev->bulk_in);
	if (dev->bulk_out_enabled)
		usb_ep_fifo_flush(dev->bulk_out);
	/* Reset the I/O buffer states and pointers, the SCSI
	 * state, and the exception.  Then invoke the handler. */
	spin_lock_irq(&dev->lock);
	/*
	for (i = 0; i < FSG_NUM_BUFFERS; ++i) {
		bh = &dev->buffhds[i];
		bh->state = BUF_STATE_EMPTY;
	}
	fsg->next_buffhd_to_fill = dev->next_buffhd_to_drain =
			&dev->buffhds[0];
	*/
	exception_req_tag = dev->exception_req_tag;
	new_config = dev->new_config;

	old_state = dev->state;
	if (old_state == MYGADGET_STATE_ABORT_BULK_OUT)
		dev->state = MYGADGET_STATE_STATUS_PHASE;
	else {
		dev->state = MYGADGET_STATE_IDLE;
	}
	spin_unlock_irq(&dev->lock);

	/* Carry out any extra actions required for the exception */
	switch (old_state) {
	case MYGADGET_STATE_ABORT_BULK_OUT:
		DBG("+++%s+++: ABORT_BULK_OUT\n", __func__);
		//send_status(dev);
		spin_lock_irq(&dev->lock);
		if (dev->state == MYGADGET_STATE_STATUS_PHASE)
			dev->state = MYGADGET_STATE_IDLE;
		spin_unlock_irq(&dev->lock);
		break;

	case MYGADGET_STATE_RESET:
		DBG("+++%s+++: RESET\n", __func__);
		/* In case we were forced against our will to halt a
		 * bulk endpoint, clear the halt now.  (The SuperH UDC
		 * requires this.) */
		if (test_and_clear_bit(IGNORE_BULK_OUT, &dev->atomic_bitflags))
			usb_ep_clear_halt(dev->bulk_in);

		if (dev->ep0_req_tag == exception_req_tag)
			mygadget_ep0_queue(dev);	// Complete the status stage
		
		break;

	case MYGADGET_STATE_INTERFACE_CHANGE:
		DBG("+++%s+++: INTERFACE_CHANGE\n", __func__);
		/*
		rc = do_set_interface(dev, 0);
		if (dev->ep0_req_tag != exception_req_tag)
			break;
		if (rc != 0)			// STALL on errors
			fsg_set_halt(dev, dev->ep0);
		else				// Complete the status stage
			ep0_queue(dev);
		*/
		break;

	case MYGADGET_STATE_CONFIG_CHANGE:
		DBG("+++%s+++: CONFIG_CHANGE\n", __func__);
		rc = mygadget_do_set_config(dev, new_config);
		if (dev->ep0_req_tag != exception_req_tag)
			break;
		if (rc != 0)			// STALL on errors
			mygadget_set_halt(dev, dev->gadget->ep0);
		else				// Complete the status stage
			mygadget_ep0_queue(dev);
		break;

	case MYGADGET_STATE_DISCONNECT:
		mygadget_do_set_config(dev, 0);		// Unconfigured state
		break;

	case MYGADGET_STATE_EXIT:
	case MYGADGET_STATE_TERMINATED:
		DBG("+++%s+++: EXIT / TERMINATED\n", __func__);
		mygadget_do_set_config(dev, 0);			// Free resources
		spin_lock_irq(&dev->lock);
		dev->state = MYGADGET_STATE_TERMINATED;	// Stop the thread
		spin_unlock_irq(&dev->lock);
		break;

	default:
		break;
	}
}

static int mygadget_sleep_thread(struct mygadget_dev *mydev)
{
	int	rc = 0;

	/* Wait until a signal arrives or we are woken up */
	while(1) {
		try_to_freeze();
		set_current_state(TASK_INTERRUPTIBLE);
		if (signal_pending(current)) {
			rc = -EINTR;
			break;
		}
		if (mydev->running)
			break;
		schedule();
	}
	__set_current_state(TASK_RUNNING);
	mydev->running = 0;
	return rc;
}

static int mygadget_thread(void *arg)
{
	struct mygadget_dev *mydev = (struct mygadget_dev *)arg;

	printk("%s\n", __func__);

	allow_signal(SIGINT);
	allow_signal(SIGTERM);
	allow_signal(SIGKILL);
	allow_signal(SIGUSR1);

	/* Allow the thread to be frozen */
	set_freezable();

	/* Arrange for userspace references to be interpreted as kernel
	 * pointers.  That way we can pass a kernel pointer to a routine
	 * that expects a __user pointer and it will work okay. */
	set_fs(get_ds());

	/* The main loop */
	while (mydev->state != MYGADGET_STATE_TERMINATED) {
		if(mydev->state > MYGADGET_STATE_IDLE || signal_pending(current)){

			DBG("%s handle exception\n", __func__);
			mygadget_handle_exception(mydev);
			continue;
		}

		DBG("%s running\n", __func__);

		if (!mydev->running) {
			mygadget_sleep_thread(mydev);
			continue;
		}

		//if(mygadget_get_next_command(mydev))
		//	continue;

		spin_lock_irq(&mydev->lock);
		if(mydev->state <= MYGADGET_STATE_IDLE)
			mydev->state = MYGADGET_STATE_IDLE;
		spin_unlock_irq(&mydev->lock);
	}

	spin_lock_irq(&mydev->lock);
	mydev->thread_task = NULL;
	spin_unlock_irq(&mydev->lock);

	/* Unregister the driver iff the thread hasn't already done so */
	if (test_and_clear_bit(REGISTERED, &gMygadget->atomic_bitflags))
		usb_gadget_unregister_driver(&mygadget_driver);
	
	/* Let the unbind and cleanup routines know the thread has exited */
	complete_and_exit(&mydev->thread_notifier, 0);
}

/* Bind函数是功能层需要实现与设备层关联的重要函数 */
static int __init mygadget_bind(struct usb_gadget *gadget)
{
	int ret = 0;
	struct mygadget_dev *mydev = gMygadget;
	struct usb_ep *ep = NULL;
	//int gcnum;

	printk("%s\n", __func__);
	
	mydev->gadget = gadget;
	set_gadget_data(gadget, mydev);
	gadget->ep0->driver_data = mydev;
	
	/* Find all the endpoints we will use */
	usb_ep_autoconfig_reset(gadget);
	ep = usb_ep_autoconfig(gadget, &hw_dev.config.vc_interf.ep_desc);
	if(!ep)
		goto autoconf_fail;
	
	ep->driver_data = mydev;		// claim the endpoint
	mydev->bulk_in = ep;

	ep = usb_ep_autoconfig(gadget, &mygadget_fs_bulk_out_desc);
	if(!ep)
		goto autoconf_fail;
	
	ep->driver_data = mydev;		// claim the endpoint
	mydev->bulk_out = ep;

	if (gadget_is_dualspeed(gadget)) {
		/* Assume ep0 uses the same maxpacket value for both speeds */
		mygadget_dev_qualifier.bMaxPacketSize0 = gadget->ep0->maxpacket;


		/* Assume endpoint addresses are the same for both speeds */
		mygadget_hs_bulk_out_desc.bEndpointAddress =
			mygadget_fs_bulk_out_desc.bEndpointAddress;

	}

	/* Allocate the request and buffer for endpoint 0 */
	mydev->ep0req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
	if(!mydev->ep0req)
		goto error;
	
	mydev->ep0req->buf = kmalloc(EP0_BUFSIZE, GFP_KERNEL);
	if (!mydev->ep0req->buf)
		goto error;

	mydev->ep0req->complete = mygadget_ep0_complete;

	/* This should reflect the actual gadget power source */
	usb_gadget_set_selfpowered(gadget);

	/* kernel thread */
	mydev->thread_task = kthread_create(mygadget_thread, mydev, "mygadget");
	if (IS_ERR(mydev->thread_task)) {
		ret = PTR_ERR(mydev->thread_task);
		goto error;
	}

	DBG("I/O thread pid: %d\n", task_pid_nr(mydev->thread_task));

	set_bit(REGISTERED, &mydev->atomic_bitflags);

	/* Tell the thread to start working */
	wake_up_process(mydev->thread_task);

	//gcnum = usb_gadget_controller_number(gadget);
	//if(gcnum >= 0)
	//	hw_dev.desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);

	//usb_gadget_set_selfpowered(gadget);

	return 0;

autoconf_fail:
	DBG("unable to autoconfigure all endpoints\n");
	ret = -ENOTSUPP;
error:
	mydev->state = MYGADGET_STATE_TERMINATED;	// The thread is dead
	mygadget_unbind(gadget);
	complete(&mydev->thread_notifier);
	return ret;
}

/* called by uvc_probe() in uvc/uvc_driver.c */
static int mygadget_init(void)
{
	int		rc;

	printk("%s\n", __func__);

	use_hw = 1;
		
	gMygadget = kzalloc(sizeof(struct mygadget_dev), GFP_KERNEL);
	if (!gMygadget)
		return -ENOMEM;
	
	spin_lock_init(&gMygadget->lock);
	init_completion(&gMygadget->thread_notifier);

	if((rc = usb_gadget_probe_driver(&mygadget_driver, mygadget_bind)) != 0) {
		kfree(gMygadget);
		gMygadget = NULL;
	}
	
	return rc;
}

/* called by uvc_disconnect() in uvc/uvc_driver.c */
static void mygadget_remove(void)
{
	printk("%s\n", __func__);

	/* Unregister the driver iff the thread hasn't already done so */
	if (test_and_clear_bit(REGISTERED, &gMygadget->atomic_bitflags))
		usb_gadget_unregister_driver(&mygadget_driver);

	/* Wait for the thread to finish up */
	wait_for_completion(&gMygadget->thread_notifier);

	kfree(gMygadget);
	gMygadget = NULL;

	use_hw = 0;
}

void dump_dev_desc(int id, struct usb_config_descriptor desc)
{
	printk("[config %d]:\n"
			"  bLength            : %u\n"
			"  bDescriptorType    : %u\n"
			"  bNumInterfaces     : %u\n"
			"  bConfigurationValue: %u\n"
			"  iConfiguration     : %u\n"
			"  bmAttributes       : %02x\n",
			id,
			desc.bLength, 
			desc.bDescriptorType,
			desc.bNumInterfaces,
			desc.bConfigurationValue,
			desc.iConfiguration,
			desc.bmAttributes);
}

void dump_intf_assoc_desc(struct usb_interface_assoc_descriptor intf_assoc_desc) 
{
	printk(" [intf_assoc_desc]:\n"
			"   bLength          : %u\n"
			"   bDescriptorType  : %u\n"
			"   bFirstInterface  : %u\n"
			"   bInterfaceCount  : %u\n"
			"   bFunctionClass   : %u\n"
			"   bFunctionSubClass: %u\n"
			"   bFunctionProtocol: %u\n"
			"   iFunction        : %u\n",
			intf_assoc_desc.bLength, 
			intf_assoc_desc.bDescriptorType,
			intf_assoc_desc.bFirstInterface,
			intf_assoc_desc.bInterfaceCount,
			intf_assoc_desc.bFunctionClass,
			intf_assoc_desc.bFunctionSubClass,
			intf_assoc_desc.bFunctionProtocol,
			intf_assoc_desc.iFunction);
}

void dump_intf_desc(int id, const char *str, struct usb_interface_descriptor intf_desc) 
{
	printk(" [intf_desc %d %s]:\n"
			"   bLength           : %u\n"
			"   bDescriptorType   : %u\n"
			"   bInterfaceNumber  : %u\n"
			"   bAlternateSetting : %u\n"
			"   bNumEndpoints     : %u\n"
			"   bInterfaceClass   : %u\n"
			"   bInterfaceSubClass: %u\n"
			"   bInterfaceProtocol: %u\n"
			"   iInterface        : %u\n",
			id, str,
			intf_desc.bLength, 
			intf_desc.bDescriptorType,
			intf_desc.bInterfaceNumber,
			intf_desc.bAlternateSetting,
			intf_desc.bNumEndpoints,
			intf_desc.bInterfaceClass,
			intf_desc.bInterfaceSubClass,
			intf_desc.bInterfaceProtocol,
			intf_desc.iInterface);
}

void dump_vc_int_desc(struct vc_interface vc_intf) 
{
	printk("  [vc intf header desc]:\n"
			"    bLength           : %02x\n"
			"    bDescriptorType   : %02x\n"
			"    bDescriptorSubType: %02x\n"
			"    bcdUVC            : %04x\n"
			"    wTotalLength      : %04x\n"
			"    dwClockFrequency  : %08x\n"
			"    bInCollection     : %02x\n"
			"    baInterfaceNr     : %02x\n",
			vc_intf.header_desc.bLength,
			vc_intf.header_desc.bDescriptorType,
			vc_intf.header_desc.bDescriptorSubType,
			vc_intf.header_desc.bcdUVC,
			vc_intf.header_desc.wTotalLength,
			vc_intf.header_desc.dwClockFrequency,
			vc_intf.header_desc.bInCollection,
			vc_intf.header_desc.baInterfaceNr);

	printk("  [vc intf it desc]:\n"
			"    bLength                 : %02x\n"
			"    bDescriptorType         : %02x\n"
			"    bDescriptorSubType      : %02x\n"
			"    bTerminalID             : %02x\n"
			"    wTerminalType           : %04x\n"
			"    bAssocTerminal          : %02x\n"
			"    iTerminal               : %02x\n"
			"    wObjectiveFocalLengthMin: %04x\n"
			"    wObjectiveFocalLengthMax: %04x\n"
			"    wOcularFocalLength      : %04x\n"
			"    bControlSize            : %02x\n"
			"    bmControls              : %06x\n",
			vc_intf.it_desc.bLength,
			vc_intf.it_desc.bDescriptorType,
			vc_intf.it_desc.bDescriptorSubType,
			vc_intf.it_desc.bTerminalID,
			vc_intf.it_desc.wTerminalType,
			vc_intf.it_desc.bAssocTerminal,
			vc_intf.it_desc.iTerminal,
			vc_intf.it_desc.wObjectiveFocalLengthMin,
			vc_intf.it_desc.wObjectiveFocalLengthMax,
			vc_intf.it_desc.wOcularFocalLength,
			vc_intf.it_desc.bControlSize,
			vc_intf.it_desc.bmControls);

	printk("  [vc intf eu desc]:\n"
			"    bLength           : %02x\n"
			"    bDescriptorType   : %02x\n"
			"    bDescriptorSubType: %02x\n"
			"    bUnitID           : %02x\n"
//			"    guidExtensionCode : %s\n"
			"    bNumControls      : %02x\n"
			"    bNrPins           : %02x\n"
			"    baSourceID        : %02x\n"
			"    bControlSize      : %02x\n"
			"    bmControls        : %02x\n"
			"    iExtension        : %02x\n",
			vc_intf.eu_desc.bLength,
			vc_intf.eu_desc.bDescriptorType,
			vc_intf.eu_desc.bDescriptorSubType,
			vc_intf.eu_desc.bUnitID,
//			vc_intf.eu_desc.guidExtensionCode,
			vc_intf.eu_desc.bNumControls,
			vc_intf.eu_desc.bNrPins,
			vc_intf.eu_desc.baSourceID,
			vc_intf.eu_desc.bControlSize,
			vc_intf.eu_desc.bmControls,
			vc_intf.eu_desc.iExtension);

	printk("  [vc intf pu desc]:\n"
			"    bLength           : %02x\n"
			"    bDescriptorType   : %02x\n"
			"    bDescriptorSubType: %02x\n"
			"    bUnitID           : %02x\n"
			"    baSourceID        : %02x\n"
			"    wMaxMultiplier    : %04x\n"
			"    bControlSize      : %02x\n"
			"    bmControls        : %04x\n"
			"    iProcessing       : %02x\n",
			vc_intf.pu_desc.bLength,
			vc_intf.pu_desc.bDescriptorType,
			vc_intf.pu_desc.bDescriptorSubType,
			vc_intf.pu_desc.bUnitID,
			vc_intf.pu_desc.baSourceID,
			vc_intf.pu_desc.wMaxMultiplier,
			vc_intf.pu_desc.bControlSize,
			vc_intf.pu_desc.bmControls,
			vc_intf.pu_desc.iProcessing);

	printk("  [vc intf ot desc]:\n"
			"    bLength           : %02x\n"
			"    bDescriptorType   : %02x\n"
			"    bDescriptorSubType: %02x\n"
			"    bTerminalID       : %02x\n"
			"    wTerminalType     : %04x\n"
			"    bAssocTerminal    : %02x\n"
			"    bSourceID         : %02x\n"
			"    iTerminal         : %02x\n",
			vc_intf.ot_desc.bLength,
			vc_intf.ot_desc.bDescriptorType,
			vc_intf.ot_desc.bDescriptorSubType,
			vc_intf.ot_desc.bTerminalID,
			vc_intf.ot_desc.wTerminalType,
			vc_intf.ot_desc.bAssocTerminal,
			vc_intf.ot_desc.bSourceID,
			vc_intf.ot_desc.iTerminal);
}


void dump_ep_desc(struct usb_endpoint_descriptor ep_desc) 
{
	printk("  [ep_desc]:\n"
			"    bLength         : %u\n"
			"    bDescriptorType : %u\n"
			"    bEndpointAddress: 0x%02x\n"
			"    bmAttributes    : %u\n"
			"    bInterval       : %u\n"
			"    bRefresh        : %u\n"
			"    bSynchAddress   : %u\n",
			ep_desc.bLength, 
			ep_desc.bDescriptorType,
			ep_desc.bEndpointAddress,
			ep_desc.bmAttributes,
			ep_desc.bInterval,
			ep_desc.bRefresh,
			ep_desc.bSynchAddress);
}

void dump_vs_int_desc(struct vs_interface vs_intf) 
{
	int i = 0;
	
	printk("  [vs intf header desc]:\n"
			"    bLength            : %02x\n"
			"    bDescriptorType    : %02x\n"
			"    bDescriptorSubType : %02x\n"
			"    bNumFormats        : %02x\n"
			"    wTotalLength       : %04x\n"
			"    bEndPointAddress   : %02x\n"
			"    bmInfo             : %02x\n"
			"    bTerminalLink      : %02x\n"
			"    bStillCaptureMethod: %02x\n"
			"    bTriggerSupport    : %02x\n"
			"    bTriggerUsage      : %02x\n"
			"    bControlSize       : %02x\n"
			"    bmaControls(0)     : %02x\n"
			"    bmaControls(1)     : %02x\n",
			vs_intf.header_desc.bLength,
			vs_intf.header_desc.bDescriptorType,
			vs_intf.header_desc.bDescriptorSubType,
			vs_intf.header_desc.bNumFormats,
			vs_intf.header_desc.wTotalLength,
			vs_intf.header_desc.bEndPointAddress,
			vs_intf.header_desc.bmInfo,
			vs_intf.header_desc.bTerminalLink,
			vs_intf.header_desc.bStillCaptureMethod,
			vs_intf.header_desc.bTriggerSupport,
			vs_intf.header_desc.bTriggerUsage,
			vs_intf.header_desc.bControlSize,
			vs_intf.header_desc.bmaControls[0],
			vs_intf.header_desc.bmaControls[1]);

	printk("  [vs intf format mjpeg desc]:\n"
			"    bLength             : %02x\n"
			"    bDescriptorType     : %02x\n"
			"    bDescriptorSubType  : %02x\n"
			"    bFormatIndex        : %02x\n"
			"    bNumFrameDescriptors: %02x\n"
			"    bFlags              : %02x\n"
			"    bDefaultFrameIndex  : %02x\n"
			"    bAspectRatioX       : %02x\n"
			"    bAspectRatioY       : %02x\n"
			"    bmInterlaceFlags    : %04x\n",
			vs_intf.mjpg_fmt_desc.bLength,
			vs_intf.mjpg_fmt_desc.bDescriptorType,
			vs_intf.mjpg_fmt_desc.bDescriptorSubtype,
			vs_intf.mjpg_fmt_desc.bFormatIndex,
			vs_intf.mjpg_fmt_desc.bNumFrameDescriptors,
			vs_intf.mjpg_fmt_desc.bFlags,
			vs_intf.mjpg_fmt_desc.bDefaultFrameIndex,
			vs_intf.mjpg_fmt_desc.bAspectRatioX,
			vs_intf.mjpg_fmt_desc.bAspectRatioY,
			vs_intf.mjpg_fmt_desc.bmInterlaceFlags);

	for(i=0; i<9; i++) {
		printk("  [vs intf mjpeg frame %d desc]:\n"
			"    bLength                  : %02x\n"
			"    bDescriptorType          : %02x\n"
			"    bDescriptorSubType       : %02x\n"
			"    bFrameIndex              : %02x\n"
			"    bmCapabilities           : %02x\n"
			"    wWidth                   : %04x\n"
			"    wHeight                  : %04x\n"
			"    dwMinBitRate             : %08x\n"
			"    dwMaxBitRate             : %08x\n"
			"    dwMaxVideoFrameBufferSize: %08x\n"
			"    dwDefaultFrameInterval   : %08x\n"
			"    bFrameIntervalType       : %02x\n"
			"    dwFrameInterval          : %04x\n",
			i,
			vs_intf.mjpg_frame_desc[i].bLength,
			vs_intf.mjpg_frame_desc[i].bDescriptorType,
			vs_intf.mjpg_frame_desc[i].bDescriptorSubtype,
			vs_intf.mjpg_frame_desc[i].bFrameIndex,
			vs_intf.mjpg_frame_desc[i].bmCapabilities,
			vs_intf.mjpg_frame_desc[i].wWidth,
			vs_intf.mjpg_frame_desc[i].wHeight,
			vs_intf.mjpg_frame_desc[i].dwMinBitRate,
			vs_intf.mjpg_frame_desc[i].dwMaxBitRate,
			vs_intf.mjpg_frame_desc[i].dwMaxVideoFrameBufferSize,
			vs_intf.mjpg_frame_desc[i].dwDefaultFrameInterval,
			vs_intf.mjpg_frame_desc[i].bFrameIntervalType,
			vs_intf.mjpg_frame_desc[i].dwFrameInterval);
	}

/*
	struct vs_intf_still_desc {
	u8 bLength;
	u8 bDescriptorType;
	u8 bDescriptorSubtype;
	u8 bEndpointAddress;
	
	u8 bNumImageSizePatterns;
	struct ImageSizePattern bImageSize[8];
	u8 bNumCompressionPatterns;
};

struct vs_intf_colorFormat_desc {
	u8 bLength;
	u8 bDescriptorType;
	u8 bDescriptorSubtype;
	u8 bColorPrimaries;
	
	u8 bTransferCharacteristics;
	u8 bMatrixCoefficients;
};
*/
}

 
 
 uvc_probe (uvc_driver.c):

<pre name="code" class="cpp">#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <asm/atomic.h>
#include <asm/unaligned.h>

#include <media/v4l2-common.h>

#include "uvcvideo.h"

/* fuchch 2015/10/28 */
#include "mygadget.c"

static int otg_on;
/* --------------- */

#define DRIVER_AUTHOR		"Laurent Pinchart " \
				"<[email protected]>"
#define DRIVER_DESC		"USB Video Class driver"

unsigned int uvc_clock_param = CLOCK_MONOTONIC;
unsigned int uvc_no_drop_param;
static unsigned int uvc_quirks_param = -1;
unsigned int uvc_trace_param;
unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;

/* ------------------------------------------------------------------------
 * Video formats
 */

static struct uvc_format_desc uvc_fmts[] = {
	{
		.name		= "YUV 4:2:2 (YUYV)",
		.guid		= UVC_GUID_FORMAT_YUY2,
		.fcc		= V4L2_PIX_FMT_YUYV,
	},
	{
		.name		= "YUV 4:2:2 (YUYV)",
		.guid		= UVC_GUID_FORMAT_YUY2_ISIGHT,
		.fcc		= V4L2_PIX_FMT_YUYV,
	},
	{
		.name		= "YUV 4:2:0 (NV12)",
		.guid		= UVC_GUID_FORMAT_NV12,
		.fcc		= V4L2_PIX_FMT_NV12,
	},
	{
		.name		= "MJPEG",
		.guid		= UVC_GUID_FORMAT_MJPEG,
		.fcc		= V4L2_PIX_FMT_MJPEG,
	},
	{
		.name		= "YVU 4:2:0 (YV12)",
		.guid		= UVC_GUID_FORMAT_YV12,
		.fcc		= V4L2_PIX_FMT_YVU420,
	},
	{
		.name		= "YUV 4:2:0 (I420)",
		.guid		= UVC_GUID_FORMAT_I420,
		.fcc		= V4L2_PIX_FMT_YUV420,
	},
	{
		.name		= "YUV 4:2:0 (M420)",
		.guid		= UVC_GUID_FORMAT_M420,
		.fcc		= V4L2_PIX_FMT_M420,
	},
	{
		.name		= "YUV 4:2:2 (UYVY)",
		.guid		= UVC_GUID_FORMAT_UYVY,
		.fcc		= V4L2_PIX_FMT_UYVY,
	},
	{
		.name		= "Greyscale (8-bit)",
		.guid		= UVC_GUID_FORMAT_Y800,
		.fcc		= V4L2_PIX_FMT_GREY,
	},
	{
		.name		= "Greyscale (16-bit)",
		.guid		= UVC_GUID_FORMAT_Y16,
		.fcc		= V4L2_PIX_FMT_Y16,
	},
	{
		.name		= "RGB Bayer",
		.guid		= UVC_GUID_FORMAT_BY8,
		.fcc		= V4L2_PIX_FMT_SBGGR8,
	},
	{
		.name		= "RGB565",
		.guid		= UVC_GUID_FORMAT_RGBP,
		.fcc		= V4L2_PIX_FMT_RGB565,
	},
};

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

struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
		__u8 epaddr)
{
	struct usb_host_endpoint *ep;
	unsigned int i;

	for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
		ep = &alts->endpoint[i];
		if (ep->desc.bEndpointAddress == epaddr)
			return ep;
	}

	return NULL;
}

static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16])
{
	unsigned int len = ARRAY_SIZE(uvc_fmts);
	unsigned int i;

	for (i = 0; i < len; ++i) {
		if (memcmp(guid, uvc_fmts[i].guid, 16) == 0)
			return &uvc_fmts[i];
	}

	return NULL;
}

static __u32 uvc_colorspace(const __u8 primaries)
{
	static const __u8 colorprimaries[] = {
		0,
		V4L2_COLORSPACE_SRGB,
		V4L2_COLORSPACE_470_SYSTEM_M,
		V4L2_COLORSPACE_470_SYSTEM_BG,
		V4L2_COLORSPACE_SMPTE170M,
		V4L2_COLORSPACE_SMPTE240M,
	};

	if (primaries < ARRAY_SIZE(colorprimaries))
		return colorprimaries[primaries];

	return 0;
}

/* Simplify a fraction using a simple continued fraction decomposition. The
 * idea here is to convert fractions such as 333333/10000000 to 1/30 using
 * 32 bit arithmetic only. The algorithm is not perfect and relies upon two
 * arbitrary parameters to remove non-significative terms from the simple
 * continued fraction decomposition. Using 8 and 333 for n_terms and threshold
 * respectively seems to give nice results.
 */
void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,
		unsigned int n_terms, unsigned int threshold)
{
	uint32_t *an;
	uint32_t x, y, r;
	unsigned int i, n;

	an = kmalloc(n_terms * sizeof *an, GFP_KERNEL);
	if (an == NULL)
		return;

	/* Convert the fraction to a simple continued fraction. See
	 * http://mathforum.org/dr.math/faq/faq.fractions.html
	 * Stop if the current term is bigger than or equal to the given
	 * threshold.
	 */
	x = *numerator;
	y = *denominator;

	for (n = 0; n < n_terms && y != 0; ++n) {
		an[n] = x / y;
		if (an[n] >= threshold) {
			if (n < 2)
				n++;
			break;
		}

		r = x - an[n] * y;
		x = y;
		y = r;
	}

	/* Expand the simple continued fraction back to an integer fraction. */
	x = 0;
	y = 1;

	for (i = n; i > 0; --i) {
		r = y;
		y = an[i-1] * y + x;
		x = r;
	}

	*numerator = y;
	*denominator = x;
	kfree(an);
}

/* Convert a fraction to a frame interval in 100ns multiples. The idea here is
 * to compute numerator / denominator * 10000000 using 32 bit fixed point
 * arithmetic only.
 */
uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator)
{
	uint32_t multiplier;

	/* Saturate the result if the operation would overflow. */
	if (denominator == 0 ||
	    numerator/denominator >= ((uint32_t)-1)/10000000)
		return (uint32_t)-1;

	/* Divide both the denominator and the multiplier by two until
	 * numerator * multiplier doesn't overflow. If anyone knows a better
	 * algorithm please let me know.
	 */
	multiplier = 10000000;
	while (numerator > ((uint32_t)-1)/multiplier) {
		multiplier /= 2;
		denominator /= 2;
	}

	return denominator ? numerator * multiplier / denominator : 0;
}

/* ------------------------------------------------------------------------
 * Terminal and unit management
 */

struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id)
{
	struct uvc_entity *entity;

	list_for_each_entry(entity, &dev->entities, list) {
		if (entity->id == id)
			return entity;
	}

	return NULL;
}

static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
	int id, struct uvc_entity *entity)
{
	unsigned int i;

	if (entity == NULL)
		entity = list_entry(&dev->entities, struct uvc_entity, list);

	list_for_each_entry_continue(entity, &dev->entities, list) {
		for (i = 0; i < entity->bNrInPins; ++i)
			if (entity->baSourceID[i] == id)
				return entity;
	}

	return NULL;
}

static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id)
{
	struct uvc_streaming *stream;

	list_for_each_entry(stream, &dev->streams, list) {
		if (stream->header.bTerminalLink == id)
			return stream;
	}

	return NULL;
}

/* ------------------------------------------------------------------------
 * Descriptors parsing
 */

static int uvc_parse_format(struct uvc_device *dev,
	struct uvc_streaming *streaming, struct uvc_format *format,
	__u32 **intervals, unsigned char *buffer, int buflen)
{
	struct usb_interface *intf = streaming->intf;
	struct usb_host_interface *alts = intf->cur_altsetting;
	struct uvc_format_desc *fmtdesc;
	struct uvc_frame *frame;
	const unsigned char *start = buffer;
	unsigned int interval;
	unsigned int i, n;
	__u8 ftype;

	format->type = buffer[2];
	format->index = buffer[3];

	switch (buffer[2]) {
	case UVC_VS_FORMAT_UNCOMPRESSED:
	case UVC_VS_FORMAT_FRAME_BASED:
		n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28;
		if (buflen < n) {
			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
			       "interface %d FORMAT error\n",
			       dev->udev->devnum,
			       alts->desc.bInterfaceNumber);
			return -EINVAL;
		}

		/* Find the format descriptor from its GUID. */
		fmtdesc = uvc_format_by_guid(&buffer[5]);

		if (fmtdesc != NULL) {
			strlcpy(format->name, fmtdesc->name,
				sizeof format->name);
			format->fcc = fmtdesc->fcc;
		} else {
			uvc_printk(KERN_INFO, "Unknown video format %pUl\n",
				&buffer[5]);
			snprintf(format->name, sizeof(format->name), "%pUl\n",
				&buffer[5]);
			format->fcc = 0;
		}

		format->bpp = buffer[21];
		if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) {
			ftype = UVC_VS_FRAME_UNCOMPRESSED;
		} else {
			ftype = UVC_VS_FRAME_FRAME_BASED;
			if (buffer[27])
				format->flags = UVC_FMT_FLAG_COMPRESSED;
		}
		break;

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

		strlcpy(format->name, "MJPEG", sizeof format->name);
		format->fcc = V4L2_PIX_FMT_MJPEG;
		format->flags = UVC_FMT_FLAG_COMPRESSED;
		format->bpp = 0;
		ftype = UVC_VS_FRAME_MJPEG;
		break;

	case UVC_VS_FORMAT_DV:
		if (buflen < 9) {
			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
			       "interface %d FORMAT error\n",
			       dev->udev->devnum,
			       alts->desc.bInterfaceNumber);
			return -EINVAL;
		}

		switch (buffer[8] & 0x7f) {
		case 0:
			strlcpy(format->name, "SD-DV", sizeof format->name);
			break;
		case 1:
			strlcpy(format->name, "SDL-DV", sizeof format->name);
			break;
		case 2:
			strlcpy(format->name, "HD-DV", sizeof format->name);
			break;
		default:
			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
			       "interface %d: unknown DV format %u\n",
			       dev->udev->devnum,
			       alts->desc.bInterfaceNumber, buffer[8]);
			return -EINVAL;
		}

		strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz",
			sizeof format->name);

		format->fcc = V4L2_PIX_FMT_DV;
		format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM;
		format->bpp = 0;
		ftype = 0;

		/* Create a dummy frame descriptor. */
		frame = &format->frame[0];
		memset(&format->frame[0], 0, sizeof format->frame[0]);
		frame->bFrameIntervalType = 1;
		frame->dwDefaultFrameInterval = 1;
		frame->dwFrameInterval = *intervals;
		*(*intervals)++ = 1;
		format->nframes = 1;
		break;

	case UVC_VS_FORMAT_MPEG2TS:
	case UVC_VS_FORMAT_STREAM_BASED:
		/* Not supported yet. */
	default:
		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
		       "interface %d unsupported format %u\n",
		       dev->udev->devnum, alts->desc.bInterfaceNumber,
		       buffer[2]);
		return -EINVAL;
	}

	uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name);

	buflen -= buffer[0];
	buffer += buffer[0];

	/* Parse the frame descriptors. Only uncompressed, MJPEG and frame
	 * based formats have frame descriptors.
	 */
	while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
	       buffer[2] == ftype) {
		frame = &format->frame[format->nframes];
		if (ftype != UVC_VS_FRAME_FRAME_BASED)
			n = buflen > 25 ? buffer[25] : 0;
		else
			n = buflen > 21 ? buffer[21] : 0;

		n = n ? n : 3;

		if (buflen < 26 + 4*n) {
			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
			       "interface %d FRAME error\n", dev->udev->devnum,
			       alts->desc.bInterfaceNumber);
			return -EINVAL;
		}

		frame->bFrameIndex = buffer[3];
		frame->bmCapabilities = buffer[4];
		frame->wWidth = get_unaligned_le16(&buffer[5]);
		frame->wHeight = get_unaligned_le16(&buffer[7]);
		frame->dwMinBitRate = get_unaligned_le32(&buffer[9]);
		frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]);
		if (ftype != UVC_VS_FRAME_FRAME_BASED) {
			frame->dwMaxVideoFrameBufferSize =
				get_unaligned_le32(&buffer[17]);
			frame->dwDefaultFrameInterval =
				get_unaligned_le32(&buffer[21]);
			frame->bFrameIntervalType = buffer[25];
		} else {
			frame->dwMaxVideoFrameBufferSize = 0;
			frame->dwDefaultFrameInterval =
				get_unaligned_le32(&buffer[17]);
			frame->bFrameIntervalType = buffer[21];
		}
		frame->dwFrameInterval = *intervals;

		/* Several UVC chipsets screw up dwMaxVideoFrameBufferSize
		 * completely. Observed behaviours range from setting the
		 * value to 1.1x the actual frame size to hardwiring the
		 * 16 low bits to 0. This results in a higher than necessary
		 * memory usage as well as a wrong image size information. For
		 * uncompressed formats this can be fixed by computing the
		 * value from the frame size.
		 */
		if (!(format->flags & UVC_FMT_FLAG_COMPRESSED))
			frame->dwMaxVideoFrameBufferSize = format->bpp
				* frame->wWidth * frame->wHeight / 8;

		/* Some bogus devices report dwMinFrameInterval equal to
		 * dwMaxFrameInterval and have dwFrameIntervalStep set to
		 * zero. Setting all null intervals to 1 fixes the problem and
		 * some other divisions by zero that could happen.
		 */
		for (i = 0; i < n; ++i) {
			interval = get_unaligned_le32(&buffer[26+4*i]);
			*(*intervals)++ = interval ? interval : 1;
		}

		/* Make sure that the default frame interval stays between
		 * the boundaries.
		 */
		n -= frame->bFrameIntervalType ? 1 : 2;
		frame->dwDefaultFrameInterval =
			min(frame->dwFrameInterval[n],
			    max(frame->dwFrameInterval[0],
				frame->dwDefaultFrameInterval));

		if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) {
			frame->bFrameIntervalType = 1;
			frame->dwFrameInterval[0] =
				frame->dwDefaultFrameInterval;
		}

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

		format->nframes++;
		buflen -= buffer[0];
		buffer += buffer[0];
	}

	if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
	    buffer[2] == UVC_VS_STILL_IMAGE_FRAME) {
		buflen -= buffer[0];
		buffer += buffer[0];
	}

	if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
	    buffer[2] == UVC_VS_COLORFORMAT) {
		if (buflen < 6) {
			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
			       "interface %d COLORFORMAT error\n",
			       dev->udev->devnum,
			       alts->desc.bInterfaceNumber);
			return -EINVAL;
		}

		format->colorspace = uvc_colorspace(buffer[3]);

		buflen -= buffer[0];
		buffer += buffer[0];
	}

	return buffer - start;
}

static int uvc_parse_streaming(struct uvc_device *dev,
	struct usb_interface *intf)
{
	struct uvc_streaming *streaming = NULL;
	struct uvc_format *format;
	struct uvc_frame *frame;
	struct usb_host_interface *alts = &intf->altsetting[0];
	unsigned char *_buffer, *buffer = alts->extra;
	int _buflen, buflen = alts->extralen;
	unsigned int nformats = 0, nframes = 0, nintervals = 0;
	unsigned int size, i, n, p;
	__u32 *interval;
	__u16 psize;
	int ret = -EINVAL;

	if (intf->cur_altsetting->desc.bInterfaceSubClass
		!= UVC_SC_VIDEOSTREAMING) {
		uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a "
			"video streaming interface\n", dev->udev->devnum,
			intf->altsetting[0].desc.bInterfaceNumber);
		return -EINVAL;
	}

	if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) {
		uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already "
			"claimed\n", dev->udev->devnum,
			intf->altsetting[0].desc.bInterfaceNumber);
		return -EINVAL;
	}

	streaming = kzalloc(sizeof *streaming, GFP_KERNEL);
	if (streaming == NULL) {
		usb_driver_release_interface(&uvc_driver.driver, intf);
		return -EINVAL;
	}

	mutex_init(&streaming->mutex);
	streaming->dev = dev;
	streaming->intf = usb_get_intf(intf);
	streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;

	/* The Pico iMage webcam has its class-specific interface descriptors
	 * after the endpoint descriptors.
	 */
	if (buflen == 0) {
		for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
			struct usb_host_endpoint *ep = &alts->endpoint[i];

			if (ep->extralen == 0)
				continue;

			if (ep->extralen > 2 &&
			    ep->extra[1] == USB_DT_CS_INTERFACE) {
				uvc_trace(UVC_TRACE_DESCR, "trying extra data "
					"from endpoint %u.\n", i);
				buffer = alts->endpoint[i].extra;
				buflen = alts->endpoint[i].extralen;
				break;
			}
		}
	}

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

	if (buflen <= 2) {
		uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming "
			"interface descriptors found.\n");
		goto error;
	}

	/* Parse the header descriptor. */
	switch (buffer[2]) {
	case UVC_VS_OUTPUT_HEADER:
		streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
		size = 9;
		break;

	case UVC_VS_INPUT_HEADER:
		streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		size = 13;
		break;

	default:
		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
			"%d HEADER descriptor not found.\n", dev->udev->devnum,
			alts->desc.bInterfaceNumber);
		goto error;
	}

	p = buflen >= 4 ? buffer[3] : 0;
	n = buflen >= size ? buffer[size-1] : 0;

	if (buflen < size + p*n) {
		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
			"interface %d HEADER descriptor is invalid.\n",
			dev->udev->devnum, alts->desc.bInterfaceNumber);
		goto error;
	}

	streaming->header.bNumFormats = p;
	streaming->header.bEndpointAddress = buffer[6];
	if (buffer[2] == UVC_VS_INPUT_HEADER) {
		streaming->header.bmInfo = buffer[7];
		streaming->header.bTerminalLink = buffer[8];
		streaming->header.bStillCaptureMethod = buffer[9];
		streaming->header.bTriggerSupport = buffer[10];
		streaming->header.bTriggerUsage = buffer[11];
	} else {
		streaming->header.bTerminalLink = buffer[7];
	}
	streaming->header.bControlSize = n;

	streaming->header.bmaControls = kmemdup(&buffer[size], p * n,
						GFP_KERNEL);
	if (streaming->header.bmaControls == NULL) {
		ret = -ENOMEM;
		goto error;
	}

	buflen -= buffer[0];
	buffer += buffer[0];

	_buffer = buffer;
	_buflen = buflen;

	/* Count the format and frame descriptors. */
	while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) {
		switch (_buffer[2]) {
		case UVC_VS_FORMAT_UNCOMPRESSED:
		case UVC_VS_FORMAT_MJPEG:
		case UVC_VS_FORMAT_FRAME_BASED:
			nformats++;
			break;

		case UVC_VS_FORMAT_DV:
			/* DV format has no frame descriptor. We will create a
			 * dummy frame descriptor with a dummy frame interval.
			 */
			nformats++;
			nframes++;
			nintervals++;
			break;

		case UVC_VS_FORMAT_MPEG2TS:
		case UVC_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 UVC_VS_FRAME_UNCOMPRESSED:
		case UVC_VS_FRAME_MJPEG:
			nframes++;
			if (_buflen > 25)
				nintervals += _buffer[25] ? _buffer[25] : 3;
			break;

		case UVC_VS_FRAME_FRAME_BASED:
			nframes++;
			if (_buflen > 21)
				nintervals += _buffer[21] ? _buffer[21] : 3;
			break;
		}

		_buflen -= _buffer[0];
		_buffer += _buffer[0];
	}

	if (nformats == 0) {
		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
			"%d has no supported formats defined.\n",
			dev->udev->devnum, alts->desc.bInterfaceNumber);
		goto error;
	}

	size = nformats * sizeof *format + nframes * sizeof *frame
	     + nintervals * sizeof *interval;
	format = kzalloc(size, GFP_KERNEL);
	if (format == NULL) {
		ret = -ENOMEM;
		goto error;
	}

	frame = (struct uvc_frame *)&format[nformats];
	interval = (__u32 *)&frame[nframes];

	streaming->format = format;
	streaming->nformats = nformats;

	/* Parse the format descriptors. */
	while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) {
		switch (buffer[2]) {
		case UVC_VS_FORMAT_UNCOMPRESSED:
		case UVC_VS_FORMAT_MJPEG:
		case UVC_VS_FORMAT_DV:
		case UVC_VS_FORMAT_FRAME_BASED:
			format->frame = frame;
			ret = uvc_parse_format(dev, streaming, format,
				&interval, buffer, buflen);
			if (ret < 0)
				goto error;

			frame += format->nframes;
			format++;

			buflen -= ret;
			buffer += ret;
			continue;

		default:
			break;
		}

		buflen -= buffer[0];
		buffer += buffer[0];
	}

	if (buflen)
		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
			"%d has %u bytes of trailing descriptor garbage.\n",
			dev->udev->devnum, alts->desc.bInterfaceNumber, buflen);

	/* Parse the alternate settings to find the maximum bandwidth. */
	for (i = 0; i < intf->num_altsetting; ++i) {
		struct usb_host_endpoint *ep;
		alts = &intf->altsetting[i];
		ep = uvc_find_endpoint(alts,
				streaming->header.bEndpointAddress);
		if (ep == NULL)
			continue;

		psize = le16_to_cpu(ep->desc.wMaxPacketSize);
		psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
		if (psize > streaming->maxpsize)
			streaming->maxpsize = psize;
	}

	list_add_tail(&streaming->list, &dev->streams);
	return 0;

error:
	usb_driver_release_interface(&uvc_driver.driver, intf);
	usb_put_intf(intf);
	kfree(streaming->format);
	kfree(streaming->header.bmaControls);
	kfree(streaming);
	return ret;
}

static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id,
		unsigned int num_pads, unsigned int extra_size)
{
	struct uvc_entity *entity;
	unsigned int num_inputs;
	unsigned int size;
	unsigned int i;

	extra_size = ALIGN(extra_size, sizeof(*entity->pads));
	num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1;
	size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads
	     + num_inputs;
	entity = kzalloc(size, GFP_KERNEL);
	if (entity == NULL)
		return NULL;

	entity->id = id;
	entity->type = type;

	entity->num_links = 0;
	entity->num_pads = num_pads;
	entity->pads = ((void *)(entity + 1)) + extra_size;

	for (i = 0; i < num_inputs; ++i)
		entity->pads[i].flags = MEDIA_PAD_FL_SINK;
	if (!UVC_ENTITY_IS_OTERM(entity))
		entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE;

	entity->bNrInPins = num_inputs;
	entity->baSourceID = (__u8 *)(&entity->pads[num_pads]);

	return entity;
}

/* Parse vendor-specific extensions. */
static int uvc_parse_vendor_control(struct uvc_device *dev,
	const unsigned char *buffer, int buflen)
{
	struct usb_device *udev = dev->udev;
	struct usb_host_interface *alts = dev->intf->cur_altsetting;
	struct uvc_entity *unit;
	unsigned int n, p;
	int handled = 0;

	switch (le16_to_cpu(dev->udev->descriptor.idVendor)) {
	case 0x046d:		/* Logitech */
		if (buffer[1] != 0x41 || buffer[2] != 0x01)
			break;

		/* Logitech implements several vendor specific functions
		 * through vendor specific extension units (LXU).
		 *
		 * The LXU descriptors are similar to XU descriptors
		 * (see "USB Device Video Class for Video Devices", section
		 * 3.7.2.6 "Extension Unit Descriptor") with the following
		 * differences:
		 *
		 * ----------------------------------------------------------
		 * 0		bLength		1	 Number
		 *	Size of this descriptor, in bytes: 24+p+n*2
		 * ----------------------------------------------------------
		 * 23+p+n	bmControlsType	N	Bitmap
		 * 	Individual bits in the set are defined:
		 * 	0: Absolute
		 * 	1: Relative
		 *
		 * 	This bitset is mapped exactly the same as bmControls.
		 * ----------------------------------------------------------
		 * 23+p+n*2	bReserved	1	Boolean
		 * ----------------------------------------------------------
		 * 24+p+n*2	iExtension	1	Index
		 *	Index of a string descriptor that describes this
		 *	extension unit.
		 * ----------------------------------------------------------
		 */
		p = buflen >= 22 ? buffer[21] : 0;
		n = buflen >= 25 + p ? buffer[22+p] : 0;

		if (buflen < 25 + p + 2*n) {
			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
				"interface %d EXTENSION_UNIT error\n",
				udev->devnum, alts->desc.bInterfaceNumber);
			break;
		}

		unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3],
					p + 1, 2*n);
		if (unit == NULL)
			return -ENOMEM;

		memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
		unit->extension.bNumControls = buffer[20];
		memcpy(unit->baSourceID, &buffer[22], p);
		unit->extension.bControlSize = buffer[22+p];
		unit->extension.bmControls = (__u8 *)unit + sizeof(*unit);
		unit->extension.bmControlsType = (__u8 *)unit + sizeof(*unit)
					       + n;
		memcpy(unit->extension.bmControls, &buffer[23+p], 2*n);

		if (buffer[24+p+2*n] != 0)
			usb_string(udev, buffer[24+p+2*n], unit->name,
				   sizeof unit->name);
		else
			sprintf(unit->name, "Extension %u", buffer[3]);

		list_add_tail(&unit->list, &dev->entities);
		handled = 1;
		break;
	}

	return handled;
}

static int uvc_parse_standard_control(struct uvc_device *dev,
	const unsigned char *buffer, int buflen)
{
	struct usb_device *udev = dev->udev;
	struct uvc_entity *unit, *term;
	struct usb_interface *intf;
	struct usb_host_interface *alts = dev->intf->cur_altsetting;
	unsigned int i, n, p, len;
	__u16 type;

	switch (buffer[2]) {
	case UVC_VC_HEADER:
		n = buflen >= 12 ? buffer[11] : 0;

		if (buflen < 12 || buflen < 12 + n) {
			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
				"interface %d HEADER error\n", udev->devnum,
				alts->desc.bInterfaceNumber);
			return -EINVAL;
		}

		dev->uvc_version = get_unaligned_le16(&buffer[3]);
		dev->clock_frequency = get_unaligned_le32(&buffer[7]);

		/* Parse all USB Video Streaming interfaces. */
		for (i = 0; i < n; ++i) {
			intf = usb_ifnum_to_if(udev, buffer[12+i]);
			if (intf == NULL) {
				uvc_trace(UVC_TRACE_DESCR, "device %d "
					"interface %d doesn't exists\n",
					udev->devnum, i);
				continue;
			}

			uvc_parse_streaming(dev, intf);
		}
		break;

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

		/* Make sure the terminal type MSB is not null, otherwise it
		 * could be confused with a unit.
		 */
		type = get_unaligned_le16(&buffer[4]);
		if ((type & 0xff00) == 0) {
			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
				"interface %d INPUT_TERMINAL %d has invalid "
				"type 0x%04x, skipping\n", udev->devnum,
				alts->desc.bInterfaceNumber,
				buffer[3], type);
			return 0;
		}

		n = 0;
		p = 0;
		len = 8;

		if (type == UVC_ITT_CAMERA) {
			n = buflen >= 15 ? buffer[14] : 0;
			len = 15;

		} else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) {
			n = buflen >= 9 ? buffer[8] : 0;
			p = buflen >= 10 + n ? buffer[9+n] : 0;
			len = 10;
		}

		if (buflen < len + n + p) {
			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
				"interface %d INPUT_TERMINAL error\n",
				udev->devnum, alts->desc.bInterfaceNumber);
			return -EINVAL;
		}

		term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3],
					1, n + p);
		if (term == NULL)
			return -ENOMEM;

		if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) {
			term->camera.bControlSize = n;
			term->camera.bmControls = (__u8 *)term + sizeof *term;
			term->camera.wObjectiveFocalLengthMin =
				get_unaligned_le16(&buffer[8]);
			term->camera.wObjectiveFocalLengthMax =
				get_unaligned_le16(&buffer[10]);
			term->camera.wOcularFocalLength =
				get_unaligned_le16(&buffer[12]);
			memcpy(term->camera.bmControls, &buffer[15], n);
		} else if (UVC_ENTITY_TYPE(term) ==
			   UVC_ITT_MEDIA_TRANSPORT_INPUT) {
			term->media.bControlSize = n;
			term->media.bmControls = (__u8 *)term + sizeof *term;
			term->media.bTransportModeSize = p;
			term->media.bmTransportModes = (__u8 *)term
						     + sizeof *term + n;
			memcpy(term->media.bmControls, &buffer[9], n);
			memcpy(term->media.bmTransportModes, &buffer[10+n], p);
		}

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

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

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

		/* Make sure the terminal type MSB is not null, otherwise it
		 * could be confused with a unit.
		 */
		type = get_unaligned_le16(&buffer[4]);
		if ((type & 0xff00) == 0) {
			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
				"interface %d OUTPUT_TERMINAL %d has invalid "
				"type 0x%04x, skipping\n", udev->devnum,
				alts->desc.bInterfaceNumber, buffer[3], type);
			return 0;
		}

		term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3],
					1, 0);
		if (term == NULL)
			return -ENOMEM;

		memcpy(term->baSourceID, &buffer[7], 1);

		if (buffer[8] != 0)
			usb_string(udev, buffer[8], term->name,
				   sizeof term->name);
		else
			sprintf(term->name, "Output %u", buffer[3]);

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

	case UVC_VC_SELECTOR_UNIT:
		p = buflen >= 5 ? buffer[4] : 0;

		if (buflen < 5 || buflen < 6 + p) {
			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
				"interface %d SELECTOR_UNIT error\n",
				udev->devnum, alts->desc.bInterfaceNumber);
			return -EINVAL;
		}

		unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0);
		if (unit == NULL)
			return -ENOMEM;

		memcpy(unit->baSourceID, &buffer[5], p);

		if (buffer[5+p] != 0)
			usb_string(udev, buffer[5+p], unit->name,
				   sizeof unit->name);
		else
			sprintf(unit->name, "Selector %u", buffer[3]);

		list_add_tail(&unit->list, &dev->entities);
		break;

	case UVC_VC_PROCESSING_UNIT:
		n = buflen >= 8 ? buffer[7] : 0;
		p = dev->uvc_version >= 0x0110 ? 10 : 9;

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

		unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n);
		if (unit == NULL)
			return -ENOMEM;

		memcpy(unit->baSourceID, &buffer[4], 1);
		unit->processing.wMaxMultiplier =
			get_unaligned_le16(&buffer[5]);
		unit->processing.bControlSize = buffer[7];
		unit->processing.bmControls = (__u8 *)unit + sizeof *unit;
		memcpy(unit->processing.bmControls, &buffer[8], n);
		if (dev->uvc_version >= 0x0110)
			unit->processing.bmVideoStandards = buffer[9+n];

		if (buffer[8+n] != 0)
			usb_string(udev, buffer[8+n], unit->name,
				   sizeof unit->name);
		else
			sprintf(unit->name, "Processing %u", buffer[3]);

		list_add_tail(&unit->list, &dev->entities);
		break;

	case UVC_VC_EXTENSION_UNIT:
		p = buflen >= 22 ? buffer[21] : 0;
		n = buflen >= 24 + p ? buffer[22+p] : 0;

		if (buflen < 24 + p + n) {
			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
				"interface %d EXTENSION_UNIT error\n",
				udev->devnum, alts->desc.bInterfaceNumber);
			return -EINVAL;
		}

		unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n);
		if (unit == NULL)
			return -ENOMEM;

		memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
		unit->extension.bNumControls = buffer[20];
		memcpy(unit->baSourceID, &buffer[22], p);
		unit->extension.bControlSize = buffer[22+p];
		unit->extension.bmControls = (__u8 *)unit + sizeof *unit;
		memcpy(unit->extension.bmControls, &buffer[23+p], n);

		if (buffer[23+p+n] != 0)
			usb_string(udev, buffer[23+p+n], unit->name,
				   sizeof unit->name);
		else
			sprintf(unit->name, "Extension %u", buffer[3]);

		list_add_tail(&unit->list, &dev->entities);
		break;

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

	return 0;
}

static int uvc_parse_control(struct uvc_device *dev)
{
	struct usb_host_interface *alts = dev->intf->cur_altsetting;
	unsigned char *buffer = alts->extra;
	int buflen = alts->extralen;
	int ret;

	/* Parse the default alternate setting only, as the UVC specification
	 * defines a single alternate setting, the default alternate setting
	 * zero.
	 */

	while (buflen > 2) {
		if (uvc_parse_vendor_control(dev, buffer, buflen) ||
		    buffer[1] != USB_DT_CS_INTERFACE)
			goto next_descriptor;

		if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0)
			return ret;

next_descriptor:
		buflen -= buffer[0];
		buffer += buffer[0];
	}

	/* Check if the optional status endpoint is present. Built-in iSight
	 * webcams have an interrupt endpoint but spit proprietary data that
	 * don't conform to the UVC status endpoint messages. Don't try to
	 * handle the interrupt endpoint for those cameras.
	 */
	if (alts->desc.bNumEndpoints == 1 &&
	    !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) {
		struct usb_host_endpoint *ep = &alts->endpoint[0];
		struct usb_endpoint_descriptor *desc = &ep->desc;

		if (usb_endpoint_is_int_in(desc) &&
		    le16_to_cpu(desc->wMaxPacketSize) >= 8 &&
		    desc->bInterval != 0) {
			uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint "
				"(addr %02x).\n", desc->bEndpointAddress);
			dev->int_ep = ep;
		}
	}

	return 0;
}

/* ------------------------------------------------------------------------
 * UVC device scan
 */

/*
 * Scan the UVC descriptors to locate a chain starting at an Output Terminal
 * and containing the following units:
 *
 * - one or more Output Terminals (USB Streaming or Display)
 * - zero or one Processing Unit
 * - zero, one or more single-input Selector Units
 * - zero or one multiple-input Selector Units, provided all inputs are
 *   connected to input terminals
 * - zero, one or mode single-input Extension Units
 * - one or more Input Terminals (Camera, External or USB Streaming)
 *
 * The terminal and units must match on of the following structures:
 *
 * ITT_*(0) -> +---------+    +---------+    +---------+ -> TT_STREAMING(0)
 * ...         | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} |    ...
 * ITT_*(n) -> +---------+    +---------+    +---------+ -> TT_STREAMING(n)
 *
 *                 +---------+    +---------+ -> OTT_*(0)
 * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} |    ...
 *                 +---------+    +---------+ -> OTT_*(n)
 *
 * The Processing Unit and Extension Units can be in any order. Additional
 * Extension Units connected to the main chain as single-unit branches are
 * also supported. Single-input Selector Units are ignored.
 */
static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
	struct uvc_entity *entity)
{
	switch (UVC_ENTITY_TYPE(entity)) {
	case UVC_VC_EXTENSION_UNIT:
		if (uvc_trace_param & UVC_TRACE_PROBE)
			printk(" <- XU %d", entity->id);

		if (entity->bNrInPins != 1) {
			uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more "
				"than 1 input pin.\n", entity->id);
			return -1;
		}

		break;

	case UVC_VC_PROCESSING_UNIT:
		if (uvc_trace_param & UVC_TRACE_PROBE)
			printk(" <- PU %d", entity->id);

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

		chain->processing = entity;
		break;

	case UVC_VC_SELECTOR_UNIT:
		if (uvc_trace_param & UVC_TRACE_PROBE)
			printk(" <- SU %d", entity->id);

		/* Single-input selector units are ignored. */
		if (entity->bNrInPins == 1)
			break;

		if (chain->selector != NULL) {
			uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector "
				"Units in chain.\n");
			return -1;
		}

		chain->selector = entity;
		break;

	case UVC_ITT_VENDOR_SPECIFIC:
	case UVC_ITT_CAMERA:
	case UVC_ITT_MEDIA_TRANSPORT_INPUT:
		if (uvc_trace_param & UVC_TRACE_PROBE)
			printk(" <- IT %d\n", entity->id);

		break;

	case UVC_OTT_VENDOR_SPECIFIC:
	case UVC_OTT_DISPLAY:
	case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
		if (uvc_trace_param & UVC_TRACE_PROBE)
			printk(" OT %d", entity->id);

		break;

	case UVC_TT_STREAMING:
		if (UVC_ENTITY_IS_ITERM(entity)) {
			if (uvc_trace_param & UVC_TRACE_PROBE)
				printk(" <- IT %d\n", entity->id);
		} else {
			if (uvc_trace_param & UVC_TRACE_PROBE)
				printk(" OT %d", entity->id);
		}

		break;

	default:
		uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type "
			"0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
		return -1;
	}

	list_add_tail(&entity->chain, &chain->entities);
	return 0;
}

static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
	struct uvc_entity *entity, struct uvc_entity *prev)
{
	struct uvc_entity *forward;
	int found;

	/* Forward scan */
	forward = NULL;
	found = 0;

	while (1) {
		forward = uvc_entity_by_reference(chain->dev, entity->id,
			forward);
		if (forward == NULL)
			break;
		if (forward == prev)
			continue;

		switch (UVC_ENTITY_TYPE(forward)) {
		case UVC_VC_EXTENSION_UNIT:
			if (forward->bNrInPins != 1) {
				uvc_trace(UVC_TRACE_DESCR, "Extension unit %d "
					  "has more than 1 input pin.\n",
					  entity->id);
				return -EINVAL;
			}

			list_add_tail(&forward->chain, &chain->entities);
			if (uvc_trace_param & UVC_TRACE_PROBE) {
				if (!found)
					printk(" (->");

				printk(" XU %d", forward->id);
				found = 1;
			}
			break;

		case UVC_OTT_VENDOR_SPECIFIC:
		case UVC_OTT_DISPLAY:
		case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
		case UVC_TT_STREAMING:
			if (UVC_ENTITY_IS_ITERM(forward)) {
				uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
					"terminal %u.\n", forward->id);
				return -EINVAL;
			}

			list_add_tail(&forward->chain, &chain->entities);
			if (uvc_trace_param & UVC_TRACE_PROBE) {
				if (!found)
					printk(" (->");

				printk(" OT %d", forward->id);
				found = 1;
			}
			break;
		}
	}
	if (found)
		printk(")");

	return 0;
}

static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
	struct uvc_entity **_entity)
{
	struct uvc_entity *entity = *_entity;
	struct uvc_entity *term;
	int id = -EINVAL, i;

	switch (UVC_ENTITY_TYPE(entity)) {
	case UVC_VC_EXTENSION_UNIT:
	case UVC_VC_PROCESSING_UNIT:
		id = entity->baSourceID[0];
		break;

	case UVC_VC_SELECTOR_UNIT:
		/* Single-input selector units are ignored. */
		if (entity->bNrInPins == 1) {
			id = entity->baSourceID[0];
			break;
		}

		if (uvc_trace_param & UVC_TRACE_PROBE)
			printk(" <- IT");

		chain->selector = entity;
		for (i = 0; i < entity->bNrInPins; ++i) {
			id = entity->baSourceID[i];
			term = uvc_entity_by_id(chain->dev, id);
			if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
				uvc_trace(UVC_TRACE_DESCR, "Selector unit %d "
					"input %d isn't connected to an "
					"input terminal\n", entity->id, i);
				return -1;
			}

			if (uvc_trace_param & UVC_TRACE_PROBE)
				printk(" %d", term->id);

			list_add_tail(&term->chain, &chain->entities);
			uvc_scan_chain_forward(chain, term, entity);
		}

		if (uvc_trace_param & UVC_TRACE_PROBE)
			printk("\n");

		id = 0;
		break;

	case UVC_ITT_VENDOR_SPECIFIC:
	case UVC_ITT_CAMERA:
	case UVC_ITT_MEDIA_TRANSPORT_INPUT:
	case UVC_OTT_VENDOR_SPECIFIC:
	case UVC_OTT_DISPLAY:
	case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
	case UVC_TT_STREAMING:
		id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0;
		break;
	}

	if (id <= 0) {
		*_entity = NULL;
		return id;
	}

	entity = uvc_entity_by_id(chain->dev, id);
	if (entity == NULL) {
		uvc_trace(UVC_TRACE_DESCR, "Found reference to "
			"unknown entity %d.\n", id);
		return -EINVAL;
	}

	*_entity = entity;
	return 0;
}

static int uvc_scan_chain(struct uvc_video_chain *chain,
			  struct uvc_entity *term)
{
	struct uvc_entity *entity, *prev;

	uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:");

	entity = term;
	prev = NULL;

	while (entity != NULL) {
		/* Entity must not be part of an existing chain */
		if (entity->chain.next || entity->chain.prev) {
			uvc_trace(UVC_TRACE_DESCR, "Found reference to "
				"entity %d already in chain.\n", entity->id);
			return -EINVAL;
		}

		/* Process entity */
		if (uvc_scan_chain_entity(chain, entity) < 0)
			return -EINVAL;

		/* Forward scan */
		if (uvc_scan_chain_forward(chain, entity, prev) < 0)
			return -EINVAL;

		/* Backward scan */
		prev = entity;
		if (uvc_scan_chain_backward(chain, &entity) < 0)
			return -EINVAL;
	}

	return 0;
}

static unsigned int uvc_print_terms(struct list_head *terms, u16 dir,
		char *buffer)
{
	struct uvc_entity *term;
	unsigned int nterms = 0;
	char *p = buffer;

	list_for_each_entry(term, terms, chain) {
		if (!UVC_ENTITY_IS_TERM(term) ||
		    UVC_TERM_DIRECTION(term) != dir)
			continue;

		if (nterms)
			p += sprintf(p, ",");
		if (++nterms >= 4) {
			p += sprintf(p, "...");
			break;
		}
		p += sprintf(p, "%u", term->id);
	}

	return p - buffer;
}

static const char *uvc_print_chain(struct uvc_video_chain *chain)
{
	static char buffer[43];
	char *p = buffer;

	p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p);
	p += sprintf(p, " -> ");
	uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p);

	return buffer;
}

/*
 * Scan the device for video chains and register video devices.
 *
 * Chains are scanned starting at their output terminals and walked backwards.
 */
static int uvc_scan_device(struct uvc_device *dev)
{
	struct uvc_video_chain *chain;
	struct uvc_entity *term;

	list_for_each_entry(term, &dev->entities, list) {
		if (!UVC_ENTITY_IS_OTERM(term))
			continue;

		/* If the terminal is already included in a chain, skip it.
		 * This can happen for chains that have multiple output
		 * terminals, where all output terminals beside the first one
		 * will be inserted in the chain in forward scans.
		 */
		if (term->chain.next || term->chain.prev)
			continue;

		chain = kzalloc(sizeof(*chain), GFP_KERNEL);
		if (chain == NULL)
			return -ENOMEM;

		INIT_LIST_HEAD(&chain->entities);
		mutex_init(&chain->ctrl_mutex);
		chain->dev = dev;

		if (uvc_scan_chain(chain, term) < 0) {
			kfree(chain);
			continue;
		}

		uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",
			  uvc_print_chain(chain));

		list_add_tail(&chain->list, &dev->chains);
	}

	if (list_empty(&dev->chains)) {
		uvc_printk(KERN_INFO, "No valid video chain found.\n");
		return -1;
	}

	return 0;
}

/* ------------------------------------------------------------------------
 * Video device registration and unregistration
 */

/*
 * Delete the UVC device.
 *
 * Called by the kernel when the last reference to the uvc_device structure
 * is released.
 *
 * As this function is called after or during disconnect(), all URBs have
 * already been canceled by the USB core. There is no need to kill the
 * interrupt URB manually.
 */
static void uvc_delete(struct uvc_device *dev)
{
	struct list_head *p, *n;

	usb_put_intf(dev->intf);
	usb_put_dev(dev->udev);

	uvc_status_cleanup(dev);
	uvc_ctrl_cleanup_device(dev);

	if (dev->vdev.dev)
		v4l2_device_unregister(&dev->vdev);
#ifdef CONFIG_MEDIA_CONTROLLER
	if (media_devnode_is_registered(&dev->mdev.devnode))
		media_device_unregister(&dev->mdev);
#endif

	list_for_each_safe(p, n, &dev->chains) {
		struct uvc_video_chain *chain;
		chain = list_entry(p, struct uvc_video_chain, list);
		kfree(chain);
	}

	list_for_each_safe(p, n, &dev->entities) {
		struct uvc_entity *entity;
		entity = list_entry(p, struct uvc_entity, list);
#ifdef CONFIG_MEDIA_CONTROLLER
		uvc_mc_cleanup_entity(entity);
#endif
		if (entity->vdev) {
			video_device_release(entity->vdev);
			entity->vdev = NULL;
		}
		kfree(entity);
	}

	list_for_each_safe(p, n, &dev->streams) {
		struct uvc_streaming *streaming;
		streaming = list_entry(p, struct uvc_streaming, list);
		usb_driver_release_interface(&uvc_driver.driver,
			streaming->intf);
		usb_put_intf(streaming->intf);
		kfree(streaming->format);
		kfree(streaming->header.bmaControls);
		kfree(streaming);
	}

	kfree(dev);
}

static void uvc_release(struct video_device *vdev)
{
	struct uvc_streaming *stream = video_get_drvdata(vdev);
	struct uvc_device *dev = stream->dev;

	/* Decrement the registered streams count and delete the device when it
	 * reaches zero.
	 */
	if (atomic_dec_and_test(&dev->nstreams))
		uvc_delete(dev);
}

/*
 * Unregister the video devices.
 */
static void uvc_unregister_video(struct uvc_device *dev)
{
	struct uvc_streaming *stream;

	/* Unregistering all video devices might result in uvc_delete() being
	 * called from inside the loop if there's no open file handle. To avoid
	 * that, increment the stream count before iterating over the streams
	 * and decrement it when done.
	 */
	atomic_inc(&dev->nstreams);

	list_for_each_entry(stream, &dev->streams, list) {
		if (stream->vdev == NULL)
			continue;

		video_unregister_device(stream->vdev);
		stream->vdev = NULL;
	}

	/* Decrement the stream count and call uvc_delete explicitly if there
	 * are no stream left.
	 */
	if (atomic_dec_and_test(&dev->nstreams))
		uvc_delete(dev);
}

static int uvc_register_video(struct uvc_device *dev,
		struct uvc_streaming *stream)
{
	struct video_device *vdev;
	int ret;

	/* Initialize the streaming interface with default streaming
	 * parameters.
	 */
	ret = uvc_video_init(stream);
	if (ret < 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) {
		uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n",
			   ret);
		return -ENOMEM;
	}

	/* 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->v4l2_dev = &dev->vdev;
	vdev->fops = &uvc_fops;
	vdev->release = uvc_release;
	strlcpy(vdev->name, dev->name, sizeof vdev->name);

	/* Set the driver data before calling video_register_device, otherwise
	 * uvc_v4l2_open might race us.
	 */
	stream->vdev = vdev;
	video_set_drvdata(vdev, stream);

	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
	if (ret < 0) {
		uvc_printk(KERN_ERR, "Failed to register video device (%d).\n",
			   ret);
		stream->vdev = NULL;
		video_device_release(vdev);
		return ret;
	}

	atomic_inc(&dev->nstreams);
	return 0;
}

/*
 * Register all video devices in all chains.
 */
static int uvc_register_terms(struct uvc_device *dev,
	struct uvc_video_chain *chain)
{
	struct uvc_streaming *stream;
	struct uvc_entity *term;
	int ret;

	list_for_each_entry(term, &chain->entities, chain) {
		if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
			continue;

		stream = uvc_stream_by_id(dev, term->id);
		if (stream == NULL) {
			uvc_printk(KERN_INFO, "No streaming interface found "
				   "for terminal %u.", term->id);
			continue;
		}

		stream->chain = chain;
		ret = uvc_register_video(dev, stream);
		if (ret < 0)
			return ret;

		term->vdev = stream->vdev;
	}

	return 0;
}

static int uvc_register_chains(struct uvc_device *dev)
{
	struct uvc_video_chain *chain;
	int ret;

	list_for_each_entry(chain, &dev->chains, list) {
		ret = uvc_register_terms(dev, chain);
		if (ret < 0)
			return ret;

#ifdef CONFIG_MEDIA_CONTROLLER
		ret = uvc_mc_register_entities(chain);
		if (ret < 0) {
			uvc_printk(KERN_INFO, "Failed to register entites "
				"(%d).\n", ret);
		}
#endif
	}

	return 0;
}

/* ------------------------------------------------------------------------
 * USB probe, disconnect, suspend and resume
 */

static int uvc_probe(struct usb_interface *intf,
		     const struct usb_device_id *id)
{
	struct usb_device *udev = interface_to_usbdev(intf);
	struct uvc_device *dev;
	int ret;

	if (id->idVendor && id->idProduct)
		uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
				"(%04x:%04x)\n", udev->devpath, id->idVendor,
				id->idProduct);
	else
		uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
				udev->devpath);

	/* Allocate memory for the device and initialize it. */
	if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL)
		return -ENOMEM;

	INIT_LIST_HEAD(&dev->entities);
	INIT_LIST_HEAD(&dev->chains);
	INIT_LIST_HEAD(&dev->streams);
	atomic_set(&dev->nstreams, 0);
	atomic_set(&dev->users, 0);
	atomic_set(&dev->nmappings, 0);

	dev->udev = usb_get_dev(udev);
	dev->intf = usb_get_intf(intf);
	dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
	dev->quirks = (uvc_quirks_param == -1)
		    ? id->driver_info : uvc_quirks_param;

	if (udev->product != NULL)
		strlcpy(dev->name, udev->product, sizeof dev->name);
	else
		snprintf(dev->name, sizeof dev->name,
			"UVC Camera (%04x:%04x)",
			le16_to_cpu(udev->descriptor.idVendor),
			le16_to_cpu(udev->descriptor.idProduct));

	/* Parse the Video Class control descriptor. */
	if (uvc_parse_control(dev) < 0) {
		uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC "
			"descriptors.\n");
		goto error;
	}

	uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n",
		dev->uvc_version >> 8, dev->uvc_version & 0xff,
		udev->product ? udev->product : "<unnamed>",
		le16_to_cpu(udev->descriptor.idVendor),
		le16_to_cpu(udev->descriptor.idProduct));

	if (dev->quirks != id->driver_info) {
		uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module "
			"parameter for testing purpose.\n", dev->quirks);
		uvc_printk(KERN_INFO, "Please report required quirks to the "
			"linux-uvc-devel mailing list.\n");
	}

	/* Register the media and V4L2 devices. */
#ifdef CONFIG_MEDIA_CONTROLLER
	dev->mdev.dev = &intf->dev;
	strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
	if (udev->serial)
		strlcpy(dev->mdev.serial, udev->serial,
			sizeof(dev->mdev.serial));
	strcpy(dev->mdev.bus_info, udev->devpath);
	dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
	dev->mdev.driver_version = DRIVER_VERSION_NUMBER;
	if (media_device_register(&dev->mdev) < 0)
		goto error;

	dev->vdev.mdev = &dev->mdev;
#endif
	if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
		goto error;

	/* Initialize controls. */
	if (uvc_ctrl_init_device(dev) < 0)
		goto error;

	/* Scan the device for video chains. */
	if (uvc_scan_device(dev) < 0)
		goto error;

	/* Register video device nodes. */
	if (uvc_register_chains(dev) < 0)
		goto error;

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

	/* Initialize the interrupt URB. */
	if ((ret = uvc_status_init(dev)) < 0) {
		uvc_printk(KERN_INFO, "Unable to initialize the status "
			"endpoint (%d), status interrupt will not be "
			"supported.\n", ret);
	}

	uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
	usb_enable_autosuspend(udev);

	/* fuchch 2015/10/28 */
	otg_on = 1;
	if(otg_on) {
		int j = 0;
		//int vd_len = 0;
		struct usb_interface *usb_itf = NULL;
		
		memset(&hw_dev, 0, sizeof(hw_dev));

		/* device desc */
		memcpy(&hw_dev.desc, &udev->descriptor, udev->descriptor.bLength);
		uvc_printk(KERN_WARNING, "VID: 0x%04x, PID: 0x%04x, bNumConfigurations=%u\n", 
					hw_dev.desc.idVendor, hw_dev.desc.idProduct, 
					hw_dev.desc.bNumConfigurations);

		/* now our usb cam just has one config */
		if(hw_dev.desc.bNumConfigurations > 0) {
			/* config desc */
			memcpy(&hw_dev.config.desc, &(udev->actconfig->desc), 
					udev->actconfig->desc.bLength);
			//dump_dev_desc(1, hw_dev.config.desc);

			if(intf->intf_assoc) {
				/* intf_assoc desc */
				memset(&hw_dev.config.intf_assoc_desc, 0, 
						sizeof(hw_dev.config.intf_assoc_desc));
				memcpy(&hw_dev.config.intf_assoc_desc, intf->intf_assoc, 
							intf->intf_assoc->bLength);

			//	dump_intf_assoc_desc(hw_dev.config.intf_assoc_desc);
			}

			/* the struct just for ov5640 */
			/* +++++++++++++ vc interf +++++++++++++ */
			usb_itf = udev->actconfig->interface[0];
			memcpy(&hw_dev.config.vc_interf.desc, &usb_itf->cur_altsetting->desc, 
					usb_itf->cur_altsetting->desc.bLength);

			//dump_intf_desc(0, "vc", hw_dev.config.vc_interf.desc);

			/* UVC */
#if 0
			DBG("[vc interf]extra data: len=%d\n", usb_itf->cur_altsetting->extralen);
			for(i=0; i<usb_itf->cur_altsetting->extralen; i++) {
				DBG("%02x ", usb_itf->cur_altsetting->extra[i]);
			}

			DBG("\n");
#endif
			DBG("struct vs_interface 's len = %d, extralen=%d\n",
					sizeof(hw_dev.config.vc_interf.vc_intf), 
					usb_itf->cur_altsetting->extralen);

			memcpy(&hw_dev.config.vc_interf.vc_intf, usb_itf->cur_altsetting->extra,
						usb_itf->cur_altsetting->extralen);

			//dump_vc_int_desc(hw_dev.config.vc_interf.vc_intf);

			/* endpoint */
			memcpy(&hw_dev.config.vc_interf.ep_desc, 
				&usb_itf->cur_altsetting->endpoint->desc, 
				usb_itf->cur_altsetting->endpoint->desc.bLength);

			dump_ep_desc(hw_dev.config.vc_interf.ep_desc);

			memcpy(&hw_dev.config.vc_interf.ep_extra, 
				usb_itf->cur_altsetting->endpoint->extra, 
				usb_itf->cur_altsetting->endpoint->extralen);

			DBG("ep_extralen=%d, ", usb_itf->cur_altsetting->endpoint->extralen);
			for(j=0; j<usb_itf->cur_altsetting->endpoint->extralen; j++)
				DBG("%02x ", usb_itf->cur_altsetting->endpoint->extra[j]);

			DBG("\n");

			/* +++++++++++++ vs interf +++++++++++++ */
			usb_itf = udev->actconfig->interface[1];
			memcpy(&hw_dev.config.vs_interf.desc, &usb_itf->cur_altsetting->desc, 
					usb_itf->cur_altsetting->desc.bLength);

			//dump_intf_desc(0, "vs", hw_dev.config.vs_interf.desc);

			/* UVC */
#if 0
			DBG("[vs interf]extra data: len=%d\n", usb_itf->cur_altsetting->extralen);
			for(i=0; i<usb_itf->cur_altsetting->extralen; i++) {
				DBG("%02x ", usb_itf->cur_altsetting->extra[i]);
			}

			DBG("\n");
#endif
				
			DBG("struct vs_interface 's len = %d, extralen=%d\n",
					sizeof(hw_dev.config.vs_interf.vs_intf), 
					usb_itf->cur_altsetting->extralen);

			memcpy(&hw_dev.config.vs_interf.vs_intf, usb_itf->cur_altsetting->extra,
						usb_itf->cur_altsetting->extralen);
		

			for(j=0; j<8; j++) {
				memcpy(&hw_dev.config.vs_interf.altsetting[j].desc, 
					&usb_itf->altsetting[j+1].desc, 
					usb_itf->altsetting[j+1].desc.bLength);
				memcpy(&hw_dev.config.vs_interf.altsetting[j].ep_desc, 
					&usb_itf->altsetting[j+1].endpoint->desc, 
					usb_itf->altsetting[j+1].endpoint->desc.bLength);
			}

			//dump_vs_int_desc(hw_dev.config.vs_interf.vs_intf);
		}


		DBG("%s: product=%s, manufacturer=%s, serial=%s\n", __func__,
			udev->product, udev->manufacturer, udev->serial);

		hw_strings[0].id = 1;
		hw_strings[0].s = udev->manufacturer;
		hw_strings[1].id = 2;
		hw_strings[1].s = udev->product;
		hw_strings[2].id = 3;
		hw_strings[2].s = udev->manufacturer;
		hw_strings[3].id = 4;
		hw_strings[3].s = udev->serial;
		hw_strings[4].id = 0;
		hw_strings[4].s = NULL;
		
		if(mygadget_init() < 0)
			goto error;
	}
	/* --------------- */

	return 0;

error:
	uvc_unregister_video(dev);
	return -ENODEV;
}

static void uvc_disconnect(struct usb_interface *intf)
{
	struct uvc_device *dev = usb_get_intfdata(intf);
	/* Set the USB interface data to NULL. This can be done outside the
	 * lock, as there's no other reader.
	 */
	usb_set_intfdata(intf, NULL);

	if (intf->cur_altsetting->desc.bInterfaceSubClass ==
	    UVC_SC_VIDEOSTREAMING)
		return;

	dev->state |= UVC_DEV_DISCONNECTED;

	uvc_unregister_video(dev);

	if(otg_on) {
		mygadget_remove();
		memset(&hw_dev, 0, sizeof(hw_dev));
	}
}

static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
{
	struct uvc_device *dev = usb_get_intfdata(intf);
	struct uvc_streaming *stream;

	uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n",
		intf->cur_altsetting->desc.bInterfaceNumber);

	/* Controls are cached on the fly so they don't need to be saved. */
	if (intf->cur_altsetting->desc.bInterfaceSubClass ==
	    UVC_SC_VIDEOCONTROL)
		return uvc_status_suspend(dev);

	list_for_each_entry(stream, &dev->streams, list) {
		if (stream->intf == intf)
			return uvc_video_suspend(stream);
	}

	uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface "
			"mismatch.\n");
	return -EINVAL;
}

static int __uvc_resume(struct usb_interface *intf, int reset)
{
	struct uvc_device *dev = usb_get_intfdata(intf);
	struct uvc_streaming *stream;

	uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n",
		intf->cur_altsetting->desc.bInterfaceNumber);

	if (intf->cur_altsetting->desc.bInterfaceSubClass ==
	    UVC_SC_VIDEOCONTROL) {
		if (reset) {
			int ret = uvc_ctrl_resume_device(dev);

			if (ret < 0)
				return ret;
		}

		return uvc_status_resume(dev);
	}

	list_for_each_entry(stream, &dev->streams, list) {
		if (stream->intf == intf)
			return uvc_video_resume(stream);
	}

	uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface "
			"mismatch.\n");
	return -EINVAL;
}

static int uvc_resume(struct usb_interface *intf)
{
	return __uvc_resume(intf, 0);
}

static int uvc_reset_resume(struct usb_interface *intf)
{
	return __uvc_resume(intf, 1);
}

/* ------------------------------------------------------------------------
 * Module parameters
 */

static int uvc_clock_param_get(char *buffer, struct kernel_param *kp)
{
	if (uvc_clock_param == CLOCK_MONOTONIC)
		return sprintf(buffer, "CLOCK_MONOTONIC");
	else
		return sprintf(buffer, "CLOCK_REALTIME");
}

static int uvc_clock_param_set(const char *val, struct kernel_param *kp)
{
	if (strncasecmp(val, "clock_", strlen("clock_")) == 0)
		val += strlen("clock_");

	if (strcasecmp(val, "monotonic") == 0)
		uvc_clock_param = CLOCK_MONOTONIC;
	else if (strcasecmp(val, "realtime") == 0)
		uvc_clock_param = CLOCK_REALTIME;
	else
		return -EINVAL;

	return 0;
}

module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get,
		  &uvc_clock_param, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(clock, "Video buffers timestamp clock");
module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(quirks, "Forced device quirks");
module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(trace, "Trace level bitmask");
module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(timeout, "Streaming control requests timeout");

/* ------------------------------------------------------------------------
 * 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[] = {
	/* Genius eFace 2025 */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x0458,
	  .idProduct		= 0x706e,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
	/* Microsoft Lifecam NX-6000 */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x045e,
	  .idProduct		= 0x00f8,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
	/* Microsoft Lifecam VX-7000 */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x045e,
	  .idProduct		= 0x0723,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
	/* Logitech Quickcam Fusion */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x046d,
	  .idProduct		= 0x08c1,
	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0 },
	/* Logitech Quickcam Orbit MP */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x046d,
	  .idProduct		= 0x08c2,
	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0 },
	/* Logitech Quickcam Pro for Notebook */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x046d,
	  .idProduct		= 0x08c3,
	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0 },
	/* Logitech Quickcam Pro 5000 */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x046d,
	  .idProduct		= 0x08c5,
	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0 },
	/* Logitech Quickcam OEM Dell Notebook */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x046d,
	  .idProduct		= 0x08c6,
	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0 },
	/* Logitech Quickcam OEM Cisco VT Camera II */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x046d,
	  .idProduct		= 0x08c7,
	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0 },
	/* Chicony CNF7129 (Asus EEE 100HE) */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x04f2,
	  .idProduct		= 0xb071,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_RESTRICT_FRAME_RATE },
	/* Alcor Micro AU3820 (Future Boy PC USB Webcam) */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x058f,
	  .idProduct		= 0x3820,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
	/* Apple Built-In iSight */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x05ac,
	  .idProduct		= 0x8501,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info 		= UVC_QUIRK_PROBE_MINMAX
				| UVC_QUIRK_BUILTIN_ISIGHT },
	/* Genesys Logic USB 2.0 PC Camera */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x05e3,
	  .idProduct		= 0x0505,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
	/* Hercules Classic Silver */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x06f8,
	  .idProduct		= 0x300c,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_FIX_BANDWIDTH },
	/* ViMicro Vega */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x0ac8,
	  .idProduct		= 0x332d,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_FIX_BANDWIDTH },
	/* ViMicro - Minoru3D */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x0ac8,
	  .idProduct		= 0x3410,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_FIX_BANDWIDTH },
	/* ViMicro Venus - Minoru3D */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x0ac8,
	  .idProduct		= 0x3420,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_FIX_BANDWIDTH },
	/* MT6227 */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x0e8d,
	  .idProduct		= 0x0004,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_PROBE_MINMAX
				| UVC_QUIRK_PROBE_DEF },
	/* IMC Networks (Medion Akoya) */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x13d3,
	  .idProduct		= 0x5103,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
	/* JMicron USB2.0 XGA WebCam */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x152d,
	  .idProduct		= 0x0310,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
	/* Syntek (HP Spartan) */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x174f,
	  .idProduct		= 0x5212,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
	/* Syntek (Samsung Q310) */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x174f,
	  .idProduct		= 0x5931,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
	/* Syntek (Packard Bell EasyNote MX52 */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x174f,
	  .idProduct		= 0x8a12,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
	/* Syntek (Asus F9SG) */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x174f,
	  .idProduct		= 0x8a31,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
	/* Syntek (Asus U3S) */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x174f,
	  .idProduct		= 0x8a33,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
	/* Syntek (JAOtech Smart Terminal) */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x174f,
	  .idProduct		= 0x8a34,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
	/* Miricle 307K */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x17dc,
	  .idProduct		= 0x0202,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
	/* Lenovo Thinkpad SL400/SL500 */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x17ef,
	  .idProduct		= 0x480b,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
	/* Aveo Technology USB 2.0 Camera */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x1871,
	  .idProduct		= 0x0306,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_PROBE_MINMAX
				| UVC_QUIRK_PROBE_EXTRAFIELDS },
	/* Ecamm Pico iMage */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x18cd,
	  .idProduct		= 0xcafe,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_PROBE_EXTRAFIELDS },
	/* Manta MM-353 Plako */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x18ec,
	  .idProduct		= 0x3188,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
	/* FSC WebCam V30S */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x18ec,
	  .idProduct		= 0x3288,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
	/* Arkmicro unbranded */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x18ec,
	  .idProduct		= 0x3290,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_PROBE_DEF },
	/* Bodelin ProScopeHR */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_DEV_HI
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x19ab,
	  .idProduct		= 0x1000,
	  .bcdDevice_hi		= 0x0126,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_STATUS_INTERVAL },
	/* MSI StarCam 370i */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x1b3b,
	  .idProduct		= 0x2951,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
	/* SiGma Micro USB Web Camera */
	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
				| USB_DEVICE_ID_MATCH_INT_INFO,
	  .idVendor		= 0x1c4f,
	  .idProduct		= 0x3000,
	  .bInterfaceClass	= USB_CLASS_VIDEO,
	  .bInterfaceSubClass	= 1,
	  .bInterfaceProtocol	= 0,
	  .driver_info		= UVC_QUIRK_PROBE_MINMAX
				| UVC_QUIRK_IGNORE_SELECTOR_UNIT },
	/* Generic USB Video Class */
	{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
	{}
};

MODULE_DEVICE_TABLE(usb, uvc_ids);

struct uvc_driver uvc_driver = {
	.driver = {
		.name		= "uvcvideo",
		.probe		= uvc_probe,
		.disconnect	= uvc_disconnect,
		.suspend	= uvc_suspend,
		.resume		= uvc_resume,
		.reset_resume	= uvc_reset_resume,
		.id_table	= uvc_ids,
		.supports_autosuspend = 1,
	},
};

static int __init uvc_init(void)
{
	int result;

	result = usb_register(&uvc_driver.driver);
	if (result == 0)
		printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
	return result;
}

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

module_init(uvc_init);
module_exit(uvc_cleanup);

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


 
 
Makefile:

obj-m = uvcvideo.o

uvcvideo-objs  := uvc_driver.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_ctrl.o \
	uvc_status.o uvc_isight.o

KDIR=/home/ubuntu/WORKING_DIRECTORY/s5pv210/linux-3.0.8
CDIR=/home/ubuntu/WORKING_DIRECTORY/s5pv210/drivers/uvc

all:
	make -C $(KDIR) M=$(CDIR) modules

clean:
	make -C $(KDIR) M=$(CDIR) clean



你可能感兴趣的:(OTG与gadget学习(四))