xref: /openbmc/linux/drivers/media/usb/uvc/uvc_driver.c (revision 0c0d06cac63ee327ceaab4b5ffe2206574ab86bd)
1*0c0d06caSMauro Carvalho Chehab /*
2*0c0d06caSMauro Carvalho Chehab  *      uvc_driver.c  --  USB Video Class driver
3*0c0d06caSMauro Carvalho Chehab  *
4*0c0d06caSMauro Carvalho Chehab  *      Copyright (C) 2005-2010
5*0c0d06caSMauro Carvalho Chehab  *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
6*0c0d06caSMauro Carvalho Chehab  *
7*0c0d06caSMauro Carvalho Chehab  *      This program is free software; you can redistribute it and/or modify
8*0c0d06caSMauro Carvalho Chehab  *      it under the terms of the GNU General Public License as published by
9*0c0d06caSMauro Carvalho Chehab  *      the Free Software Foundation; either version 2 of the License, or
10*0c0d06caSMauro Carvalho Chehab  *      (at your option) any later version.
11*0c0d06caSMauro Carvalho Chehab  *
12*0c0d06caSMauro Carvalho Chehab  */
13*0c0d06caSMauro Carvalho Chehab 
14*0c0d06caSMauro Carvalho Chehab /*
15*0c0d06caSMauro Carvalho Chehab  * This driver aims to support video input and ouput devices compliant with the
16*0c0d06caSMauro Carvalho Chehab  * 'USB Video Class' specification.
17*0c0d06caSMauro Carvalho Chehab  *
18*0c0d06caSMauro Carvalho Chehab  * The driver doesn't support the deprecated v4l1 interface. It implements the
19*0c0d06caSMauro Carvalho Chehab  * mmap capture method only, and doesn't do any image format conversion in
20*0c0d06caSMauro Carvalho Chehab  * software. If your user-space application doesn't support YUYV or MJPEG, fix
21*0c0d06caSMauro Carvalho Chehab  * it :-). Please note that the MJPEG data have been stripped from their
22*0c0d06caSMauro Carvalho Chehab  * Huffman tables (DHT marker), you will need to add it back if your JPEG
23*0c0d06caSMauro Carvalho Chehab  * codec can't handle MJPEG data.
24*0c0d06caSMauro Carvalho Chehab  */
25*0c0d06caSMauro Carvalho Chehab 
26*0c0d06caSMauro Carvalho Chehab #include <linux/atomic.h>
27*0c0d06caSMauro Carvalho Chehab #include <linux/kernel.h>
28*0c0d06caSMauro Carvalho Chehab #include <linux/list.h>
29*0c0d06caSMauro Carvalho Chehab #include <linux/module.h>
30*0c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
31*0c0d06caSMauro Carvalho Chehab #include <linux/usb.h>
32*0c0d06caSMauro Carvalho Chehab #include <linux/videodev2.h>
33*0c0d06caSMauro Carvalho Chehab #include <linux/vmalloc.h>
34*0c0d06caSMauro Carvalho Chehab #include <linux/wait.h>
35*0c0d06caSMauro Carvalho Chehab #include <linux/version.h>
36*0c0d06caSMauro Carvalho Chehab #include <asm/unaligned.h>
37*0c0d06caSMauro Carvalho Chehab 
38*0c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h>
39*0c0d06caSMauro Carvalho Chehab 
40*0c0d06caSMauro Carvalho Chehab #include "uvcvideo.h"
41*0c0d06caSMauro Carvalho Chehab 
42*0c0d06caSMauro Carvalho Chehab #define DRIVER_AUTHOR		"Laurent Pinchart " \
43*0c0d06caSMauro Carvalho Chehab 				"<laurent.pinchart@ideasonboard.com>"
44*0c0d06caSMauro Carvalho Chehab #define DRIVER_DESC		"USB Video Class driver"
45*0c0d06caSMauro Carvalho Chehab 
46*0c0d06caSMauro Carvalho Chehab unsigned int uvc_clock_param = CLOCK_MONOTONIC;
47*0c0d06caSMauro Carvalho Chehab unsigned int uvc_no_drop_param;
48*0c0d06caSMauro Carvalho Chehab static unsigned int uvc_quirks_param = -1;
49*0c0d06caSMauro Carvalho Chehab unsigned int uvc_trace_param;
50*0c0d06caSMauro Carvalho Chehab unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
51*0c0d06caSMauro Carvalho Chehab 
52*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
53*0c0d06caSMauro Carvalho Chehab  * Video formats
54*0c0d06caSMauro Carvalho Chehab  */
55*0c0d06caSMauro Carvalho Chehab 
56*0c0d06caSMauro Carvalho Chehab static struct uvc_format_desc uvc_fmts[] = {
57*0c0d06caSMauro Carvalho Chehab 	{
58*0c0d06caSMauro Carvalho Chehab 		.name		= "YUV 4:2:2 (YUYV)",
59*0c0d06caSMauro Carvalho Chehab 		.guid		= UVC_GUID_FORMAT_YUY2,
60*0c0d06caSMauro Carvalho Chehab 		.fcc		= V4L2_PIX_FMT_YUYV,
61*0c0d06caSMauro Carvalho Chehab 	},
62*0c0d06caSMauro Carvalho Chehab 	{
63*0c0d06caSMauro Carvalho Chehab 		.name		= "YUV 4:2:2 (YUYV)",
64*0c0d06caSMauro Carvalho Chehab 		.guid		= UVC_GUID_FORMAT_YUY2_ISIGHT,
65*0c0d06caSMauro Carvalho Chehab 		.fcc		= V4L2_PIX_FMT_YUYV,
66*0c0d06caSMauro Carvalho Chehab 	},
67*0c0d06caSMauro Carvalho Chehab 	{
68*0c0d06caSMauro Carvalho Chehab 		.name		= "YUV 4:2:0 (NV12)",
69*0c0d06caSMauro Carvalho Chehab 		.guid		= UVC_GUID_FORMAT_NV12,
70*0c0d06caSMauro Carvalho Chehab 		.fcc		= V4L2_PIX_FMT_NV12,
71*0c0d06caSMauro Carvalho Chehab 	},
72*0c0d06caSMauro Carvalho Chehab 	{
73*0c0d06caSMauro Carvalho Chehab 		.name		= "MJPEG",
74*0c0d06caSMauro Carvalho Chehab 		.guid		= UVC_GUID_FORMAT_MJPEG,
75*0c0d06caSMauro Carvalho Chehab 		.fcc		= V4L2_PIX_FMT_MJPEG,
76*0c0d06caSMauro Carvalho Chehab 	},
77*0c0d06caSMauro Carvalho Chehab 	{
78*0c0d06caSMauro Carvalho Chehab 		.name		= "YVU 4:2:0 (YV12)",
79*0c0d06caSMauro Carvalho Chehab 		.guid		= UVC_GUID_FORMAT_YV12,
80*0c0d06caSMauro Carvalho Chehab 		.fcc		= V4L2_PIX_FMT_YVU420,
81*0c0d06caSMauro Carvalho Chehab 	},
82*0c0d06caSMauro Carvalho Chehab 	{
83*0c0d06caSMauro Carvalho Chehab 		.name		= "YUV 4:2:0 (I420)",
84*0c0d06caSMauro Carvalho Chehab 		.guid		= UVC_GUID_FORMAT_I420,
85*0c0d06caSMauro Carvalho Chehab 		.fcc		= V4L2_PIX_FMT_YUV420,
86*0c0d06caSMauro Carvalho Chehab 	},
87*0c0d06caSMauro Carvalho Chehab 	{
88*0c0d06caSMauro Carvalho Chehab 		.name		= "YUV 4:2:0 (M420)",
89*0c0d06caSMauro Carvalho Chehab 		.guid		= UVC_GUID_FORMAT_M420,
90*0c0d06caSMauro Carvalho Chehab 		.fcc		= V4L2_PIX_FMT_M420,
91*0c0d06caSMauro Carvalho Chehab 	},
92*0c0d06caSMauro Carvalho Chehab 	{
93*0c0d06caSMauro Carvalho Chehab 		.name		= "YUV 4:2:2 (UYVY)",
94*0c0d06caSMauro Carvalho Chehab 		.guid		= UVC_GUID_FORMAT_UYVY,
95*0c0d06caSMauro Carvalho Chehab 		.fcc		= V4L2_PIX_FMT_UYVY,
96*0c0d06caSMauro Carvalho Chehab 	},
97*0c0d06caSMauro Carvalho Chehab 	{
98*0c0d06caSMauro Carvalho Chehab 		.name		= "Greyscale 8-bit (Y800)",
99*0c0d06caSMauro Carvalho Chehab 		.guid		= UVC_GUID_FORMAT_Y800,
100*0c0d06caSMauro Carvalho Chehab 		.fcc		= V4L2_PIX_FMT_GREY,
101*0c0d06caSMauro Carvalho Chehab 	},
102*0c0d06caSMauro Carvalho Chehab 	{
103*0c0d06caSMauro Carvalho Chehab 		.name		= "Greyscale 8-bit (Y8  )",
104*0c0d06caSMauro Carvalho Chehab 		.guid		= UVC_GUID_FORMAT_Y8,
105*0c0d06caSMauro Carvalho Chehab 		.fcc		= V4L2_PIX_FMT_GREY,
106*0c0d06caSMauro Carvalho Chehab 	},
107*0c0d06caSMauro Carvalho Chehab 	{
108*0c0d06caSMauro Carvalho Chehab 		.name		= "Greyscale 10-bit (Y10 )",
109*0c0d06caSMauro Carvalho Chehab 		.guid		= UVC_GUID_FORMAT_Y10,
110*0c0d06caSMauro Carvalho Chehab 		.fcc		= V4L2_PIX_FMT_Y10,
111*0c0d06caSMauro Carvalho Chehab 	},
112*0c0d06caSMauro Carvalho Chehab 	{
113*0c0d06caSMauro Carvalho Chehab 		.name		= "Greyscale 12-bit (Y12 )",
114*0c0d06caSMauro Carvalho Chehab 		.guid		= UVC_GUID_FORMAT_Y12,
115*0c0d06caSMauro Carvalho Chehab 		.fcc		= V4L2_PIX_FMT_Y12,
116*0c0d06caSMauro Carvalho Chehab 	},
117*0c0d06caSMauro Carvalho Chehab 	{
118*0c0d06caSMauro Carvalho Chehab 		.name		= "Greyscale 16-bit (Y16 )",
119*0c0d06caSMauro Carvalho Chehab 		.guid		= UVC_GUID_FORMAT_Y16,
120*0c0d06caSMauro Carvalho Chehab 		.fcc		= V4L2_PIX_FMT_Y16,
121*0c0d06caSMauro Carvalho Chehab 	},
122*0c0d06caSMauro Carvalho Chehab 	{
123*0c0d06caSMauro Carvalho Chehab 		.name		= "RGB Bayer",
124*0c0d06caSMauro Carvalho Chehab 		.guid		= UVC_GUID_FORMAT_BY8,
125*0c0d06caSMauro Carvalho Chehab 		.fcc		= V4L2_PIX_FMT_SBGGR8,
126*0c0d06caSMauro Carvalho Chehab 	},
127*0c0d06caSMauro Carvalho Chehab 	{
128*0c0d06caSMauro Carvalho Chehab 		.name		= "RGB565",
129*0c0d06caSMauro Carvalho Chehab 		.guid		= UVC_GUID_FORMAT_RGBP,
130*0c0d06caSMauro Carvalho Chehab 		.fcc		= V4L2_PIX_FMT_RGB565,
131*0c0d06caSMauro Carvalho Chehab 	},
132*0c0d06caSMauro Carvalho Chehab 	{
133*0c0d06caSMauro Carvalho Chehab 		.name		= "H.264",
134*0c0d06caSMauro Carvalho Chehab 		.guid		= UVC_GUID_FORMAT_H264,
135*0c0d06caSMauro Carvalho Chehab 		.fcc		= V4L2_PIX_FMT_H264,
136*0c0d06caSMauro Carvalho Chehab 	},
137*0c0d06caSMauro Carvalho Chehab };
138*0c0d06caSMauro Carvalho Chehab 
139*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
140*0c0d06caSMauro Carvalho Chehab  * Utility functions
141*0c0d06caSMauro Carvalho Chehab  */
142*0c0d06caSMauro Carvalho Chehab 
143*0c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
144*0c0d06caSMauro Carvalho Chehab 		__u8 epaddr)
145*0c0d06caSMauro Carvalho Chehab {
146*0c0d06caSMauro Carvalho Chehab 	struct usb_host_endpoint *ep;
147*0c0d06caSMauro Carvalho Chehab 	unsigned int i;
148*0c0d06caSMauro Carvalho Chehab 
149*0c0d06caSMauro Carvalho Chehab 	for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
150*0c0d06caSMauro Carvalho Chehab 		ep = &alts->endpoint[i];
151*0c0d06caSMauro Carvalho Chehab 		if (ep->desc.bEndpointAddress == epaddr)
152*0c0d06caSMauro Carvalho Chehab 			return ep;
153*0c0d06caSMauro Carvalho Chehab 	}
154*0c0d06caSMauro Carvalho Chehab 
155*0c0d06caSMauro Carvalho Chehab 	return NULL;
156*0c0d06caSMauro Carvalho Chehab }
157*0c0d06caSMauro Carvalho Chehab 
158*0c0d06caSMauro Carvalho Chehab static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16])
159*0c0d06caSMauro Carvalho Chehab {
160*0c0d06caSMauro Carvalho Chehab 	unsigned int len = ARRAY_SIZE(uvc_fmts);
161*0c0d06caSMauro Carvalho Chehab 	unsigned int i;
162*0c0d06caSMauro Carvalho Chehab 
163*0c0d06caSMauro Carvalho Chehab 	for (i = 0; i < len; ++i) {
164*0c0d06caSMauro Carvalho Chehab 		if (memcmp(guid, uvc_fmts[i].guid, 16) == 0)
165*0c0d06caSMauro Carvalho Chehab 			return &uvc_fmts[i];
166*0c0d06caSMauro Carvalho Chehab 	}
167*0c0d06caSMauro Carvalho Chehab 
168*0c0d06caSMauro Carvalho Chehab 	return NULL;
169*0c0d06caSMauro Carvalho Chehab }
170*0c0d06caSMauro Carvalho Chehab 
171*0c0d06caSMauro Carvalho Chehab static __u32 uvc_colorspace(const __u8 primaries)
172*0c0d06caSMauro Carvalho Chehab {
173*0c0d06caSMauro Carvalho Chehab 	static const __u8 colorprimaries[] = {
174*0c0d06caSMauro Carvalho Chehab 		0,
175*0c0d06caSMauro Carvalho Chehab 		V4L2_COLORSPACE_SRGB,
176*0c0d06caSMauro Carvalho Chehab 		V4L2_COLORSPACE_470_SYSTEM_M,
177*0c0d06caSMauro Carvalho Chehab 		V4L2_COLORSPACE_470_SYSTEM_BG,
178*0c0d06caSMauro Carvalho Chehab 		V4L2_COLORSPACE_SMPTE170M,
179*0c0d06caSMauro Carvalho Chehab 		V4L2_COLORSPACE_SMPTE240M,
180*0c0d06caSMauro Carvalho Chehab 	};
181*0c0d06caSMauro Carvalho Chehab 
182*0c0d06caSMauro Carvalho Chehab 	if (primaries < ARRAY_SIZE(colorprimaries))
183*0c0d06caSMauro Carvalho Chehab 		return colorprimaries[primaries];
184*0c0d06caSMauro Carvalho Chehab 
185*0c0d06caSMauro Carvalho Chehab 	return 0;
186*0c0d06caSMauro Carvalho Chehab }
187*0c0d06caSMauro Carvalho Chehab 
188*0c0d06caSMauro Carvalho Chehab /* Simplify a fraction using a simple continued fraction decomposition. The
189*0c0d06caSMauro Carvalho Chehab  * idea here is to convert fractions such as 333333/10000000 to 1/30 using
190*0c0d06caSMauro Carvalho Chehab  * 32 bit arithmetic only. The algorithm is not perfect and relies upon two
191*0c0d06caSMauro Carvalho Chehab  * arbitrary parameters to remove non-significative terms from the simple
192*0c0d06caSMauro Carvalho Chehab  * continued fraction decomposition. Using 8 and 333 for n_terms and threshold
193*0c0d06caSMauro Carvalho Chehab  * respectively seems to give nice results.
194*0c0d06caSMauro Carvalho Chehab  */
195*0c0d06caSMauro Carvalho Chehab void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,
196*0c0d06caSMauro Carvalho Chehab 		unsigned int n_terms, unsigned int threshold)
197*0c0d06caSMauro Carvalho Chehab {
198*0c0d06caSMauro Carvalho Chehab 	uint32_t *an;
199*0c0d06caSMauro Carvalho Chehab 	uint32_t x, y, r;
200*0c0d06caSMauro Carvalho Chehab 	unsigned int i, n;
201*0c0d06caSMauro Carvalho Chehab 
202*0c0d06caSMauro Carvalho Chehab 	an = kmalloc(n_terms * sizeof *an, GFP_KERNEL);
203*0c0d06caSMauro Carvalho Chehab 	if (an == NULL)
204*0c0d06caSMauro Carvalho Chehab 		return;
205*0c0d06caSMauro Carvalho Chehab 
206*0c0d06caSMauro Carvalho Chehab 	/* Convert the fraction to a simple continued fraction. See
207*0c0d06caSMauro Carvalho Chehab 	 * http://mathforum.org/dr.math/faq/faq.fractions.html
208*0c0d06caSMauro Carvalho Chehab 	 * Stop if the current term is bigger than or equal to the given
209*0c0d06caSMauro Carvalho Chehab 	 * threshold.
210*0c0d06caSMauro Carvalho Chehab 	 */
211*0c0d06caSMauro Carvalho Chehab 	x = *numerator;
212*0c0d06caSMauro Carvalho Chehab 	y = *denominator;
213*0c0d06caSMauro Carvalho Chehab 
214*0c0d06caSMauro Carvalho Chehab 	for (n = 0; n < n_terms && y != 0; ++n) {
215*0c0d06caSMauro Carvalho Chehab 		an[n] = x / y;
216*0c0d06caSMauro Carvalho Chehab 		if (an[n] >= threshold) {
217*0c0d06caSMauro Carvalho Chehab 			if (n < 2)
218*0c0d06caSMauro Carvalho Chehab 				n++;
219*0c0d06caSMauro Carvalho Chehab 			break;
220*0c0d06caSMauro Carvalho Chehab 		}
221*0c0d06caSMauro Carvalho Chehab 
222*0c0d06caSMauro Carvalho Chehab 		r = x - an[n] * y;
223*0c0d06caSMauro Carvalho Chehab 		x = y;
224*0c0d06caSMauro Carvalho Chehab 		y = r;
225*0c0d06caSMauro Carvalho Chehab 	}
226*0c0d06caSMauro Carvalho Chehab 
227*0c0d06caSMauro Carvalho Chehab 	/* Expand the simple continued fraction back to an integer fraction. */
228*0c0d06caSMauro Carvalho Chehab 	x = 0;
229*0c0d06caSMauro Carvalho Chehab 	y = 1;
230*0c0d06caSMauro Carvalho Chehab 
231*0c0d06caSMauro Carvalho Chehab 	for (i = n; i > 0; --i) {
232*0c0d06caSMauro Carvalho Chehab 		r = y;
233*0c0d06caSMauro Carvalho Chehab 		y = an[i-1] * y + x;
234*0c0d06caSMauro Carvalho Chehab 		x = r;
235*0c0d06caSMauro Carvalho Chehab 	}
236*0c0d06caSMauro Carvalho Chehab 
237*0c0d06caSMauro Carvalho Chehab 	*numerator = y;
238*0c0d06caSMauro Carvalho Chehab 	*denominator = x;
239*0c0d06caSMauro Carvalho Chehab 	kfree(an);
240*0c0d06caSMauro Carvalho Chehab }
241*0c0d06caSMauro Carvalho Chehab 
242*0c0d06caSMauro Carvalho Chehab /* Convert a fraction to a frame interval in 100ns multiples. The idea here is
243*0c0d06caSMauro Carvalho Chehab  * to compute numerator / denominator * 10000000 using 32 bit fixed point
244*0c0d06caSMauro Carvalho Chehab  * arithmetic only.
245*0c0d06caSMauro Carvalho Chehab  */
246*0c0d06caSMauro Carvalho Chehab uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator)
247*0c0d06caSMauro Carvalho Chehab {
248*0c0d06caSMauro Carvalho Chehab 	uint32_t multiplier;
249*0c0d06caSMauro Carvalho Chehab 
250*0c0d06caSMauro Carvalho Chehab 	/* Saturate the result if the operation would overflow. */
251*0c0d06caSMauro Carvalho Chehab 	if (denominator == 0 ||
252*0c0d06caSMauro Carvalho Chehab 	    numerator/denominator >= ((uint32_t)-1)/10000000)
253*0c0d06caSMauro Carvalho Chehab 		return (uint32_t)-1;
254*0c0d06caSMauro Carvalho Chehab 
255*0c0d06caSMauro Carvalho Chehab 	/* Divide both the denominator and the multiplier by two until
256*0c0d06caSMauro Carvalho Chehab 	 * numerator * multiplier doesn't overflow. If anyone knows a better
257*0c0d06caSMauro Carvalho Chehab 	 * algorithm please let me know.
258*0c0d06caSMauro Carvalho Chehab 	 */
259*0c0d06caSMauro Carvalho Chehab 	multiplier = 10000000;
260*0c0d06caSMauro Carvalho Chehab 	while (numerator > ((uint32_t)-1)/multiplier) {
261*0c0d06caSMauro Carvalho Chehab 		multiplier /= 2;
262*0c0d06caSMauro Carvalho Chehab 		denominator /= 2;
263*0c0d06caSMauro Carvalho Chehab 	}
264*0c0d06caSMauro Carvalho Chehab 
265*0c0d06caSMauro Carvalho Chehab 	return denominator ? numerator * multiplier / denominator : 0;
266*0c0d06caSMauro Carvalho Chehab }
267*0c0d06caSMauro Carvalho Chehab 
268*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
269*0c0d06caSMauro Carvalho Chehab  * Terminal and unit management
270*0c0d06caSMauro Carvalho Chehab  */
271*0c0d06caSMauro Carvalho Chehab 
272*0c0d06caSMauro Carvalho Chehab struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id)
273*0c0d06caSMauro Carvalho Chehab {
274*0c0d06caSMauro Carvalho Chehab 	struct uvc_entity *entity;
275*0c0d06caSMauro Carvalho Chehab 
276*0c0d06caSMauro Carvalho Chehab 	list_for_each_entry(entity, &dev->entities, list) {
277*0c0d06caSMauro Carvalho Chehab 		if (entity->id == id)
278*0c0d06caSMauro Carvalho Chehab 			return entity;
279*0c0d06caSMauro Carvalho Chehab 	}
280*0c0d06caSMauro Carvalho Chehab 
281*0c0d06caSMauro Carvalho Chehab 	return NULL;
282*0c0d06caSMauro Carvalho Chehab }
283*0c0d06caSMauro Carvalho Chehab 
284*0c0d06caSMauro Carvalho Chehab static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
285*0c0d06caSMauro Carvalho Chehab 	int id, struct uvc_entity *entity)
286*0c0d06caSMauro Carvalho Chehab {
287*0c0d06caSMauro Carvalho Chehab 	unsigned int i;
288*0c0d06caSMauro Carvalho Chehab 
289*0c0d06caSMauro Carvalho Chehab 	if (entity == NULL)
290*0c0d06caSMauro Carvalho Chehab 		entity = list_entry(&dev->entities, struct uvc_entity, list);
291*0c0d06caSMauro Carvalho Chehab 
292*0c0d06caSMauro Carvalho Chehab 	list_for_each_entry_continue(entity, &dev->entities, list) {
293*0c0d06caSMauro Carvalho Chehab 		for (i = 0; i < entity->bNrInPins; ++i)
294*0c0d06caSMauro Carvalho Chehab 			if (entity->baSourceID[i] == id)
295*0c0d06caSMauro Carvalho Chehab 				return entity;
296*0c0d06caSMauro Carvalho Chehab 	}
297*0c0d06caSMauro Carvalho Chehab 
298*0c0d06caSMauro Carvalho Chehab 	return NULL;
299*0c0d06caSMauro Carvalho Chehab }
300*0c0d06caSMauro Carvalho Chehab 
301*0c0d06caSMauro Carvalho Chehab static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id)
302*0c0d06caSMauro Carvalho Chehab {
303*0c0d06caSMauro Carvalho Chehab 	struct uvc_streaming *stream;
304*0c0d06caSMauro Carvalho Chehab 
305*0c0d06caSMauro Carvalho Chehab 	list_for_each_entry(stream, &dev->streams, list) {
306*0c0d06caSMauro Carvalho Chehab 		if (stream->header.bTerminalLink == id)
307*0c0d06caSMauro Carvalho Chehab 			return stream;
308*0c0d06caSMauro Carvalho Chehab 	}
309*0c0d06caSMauro Carvalho Chehab 
310*0c0d06caSMauro Carvalho Chehab 	return NULL;
311*0c0d06caSMauro Carvalho Chehab }
312*0c0d06caSMauro Carvalho Chehab 
313*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
314*0c0d06caSMauro Carvalho Chehab  * Descriptors parsing
315*0c0d06caSMauro Carvalho Chehab  */
316*0c0d06caSMauro Carvalho Chehab 
317*0c0d06caSMauro Carvalho Chehab static int uvc_parse_format(struct uvc_device *dev,
318*0c0d06caSMauro Carvalho Chehab 	struct uvc_streaming *streaming, struct uvc_format *format,
319*0c0d06caSMauro Carvalho Chehab 	__u32 **intervals, unsigned char *buffer, int buflen)
320*0c0d06caSMauro Carvalho Chehab {
321*0c0d06caSMauro Carvalho Chehab 	struct usb_interface *intf = streaming->intf;
322*0c0d06caSMauro Carvalho Chehab 	struct usb_host_interface *alts = intf->cur_altsetting;
323*0c0d06caSMauro Carvalho Chehab 	struct uvc_format_desc *fmtdesc;
324*0c0d06caSMauro Carvalho Chehab 	struct uvc_frame *frame;
325*0c0d06caSMauro Carvalho Chehab 	const unsigned char *start = buffer;
326*0c0d06caSMauro Carvalho Chehab 	unsigned int interval;
327*0c0d06caSMauro Carvalho Chehab 	unsigned int i, n;
328*0c0d06caSMauro Carvalho Chehab 	__u8 ftype;
329*0c0d06caSMauro Carvalho Chehab 
330*0c0d06caSMauro Carvalho Chehab 	format->type = buffer[2];
331*0c0d06caSMauro Carvalho Chehab 	format->index = buffer[3];
332*0c0d06caSMauro Carvalho Chehab 
333*0c0d06caSMauro Carvalho Chehab 	switch (buffer[2]) {
334*0c0d06caSMauro Carvalho Chehab 	case UVC_VS_FORMAT_UNCOMPRESSED:
335*0c0d06caSMauro Carvalho Chehab 	case UVC_VS_FORMAT_FRAME_BASED:
336*0c0d06caSMauro Carvalho Chehab 		n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28;
337*0c0d06caSMauro Carvalho Chehab 		if (buflen < n) {
338*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
339*0c0d06caSMauro Carvalho Chehab 			       "interface %d FORMAT error\n",
340*0c0d06caSMauro Carvalho Chehab 			       dev->udev->devnum,
341*0c0d06caSMauro Carvalho Chehab 			       alts->desc.bInterfaceNumber);
342*0c0d06caSMauro Carvalho Chehab 			return -EINVAL;
343*0c0d06caSMauro Carvalho Chehab 		}
344*0c0d06caSMauro Carvalho Chehab 
345*0c0d06caSMauro Carvalho Chehab 		/* Find the format descriptor from its GUID. */
346*0c0d06caSMauro Carvalho Chehab 		fmtdesc = uvc_format_by_guid(&buffer[5]);
347*0c0d06caSMauro Carvalho Chehab 
348*0c0d06caSMauro Carvalho Chehab 		if (fmtdesc != NULL) {
349*0c0d06caSMauro Carvalho Chehab 			strlcpy(format->name, fmtdesc->name,
350*0c0d06caSMauro Carvalho Chehab 				sizeof format->name);
351*0c0d06caSMauro Carvalho Chehab 			format->fcc = fmtdesc->fcc;
352*0c0d06caSMauro Carvalho Chehab 		} else {
353*0c0d06caSMauro Carvalho Chehab 			uvc_printk(KERN_INFO, "Unknown video format %pUl\n",
354*0c0d06caSMauro Carvalho Chehab 				&buffer[5]);
355*0c0d06caSMauro Carvalho Chehab 			snprintf(format->name, sizeof(format->name), "%pUl\n",
356*0c0d06caSMauro Carvalho Chehab 				&buffer[5]);
357*0c0d06caSMauro Carvalho Chehab 			format->fcc = 0;
358*0c0d06caSMauro Carvalho Chehab 		}
359*0c0d06caSMauro Carvalho Chehab 
360*0c0d06caSMauro Carvalho Chehab 		format->bpp = buffer[21];
361*0c0d06caSMauro Carvalho Chehab 		if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) {
362*0c0d06caSMauro Carvalho Chehab 			ftype = UVC_VS_FRAME_UNCOMPRESSED;
363*0c0d06caSMauro Carvalho Chehab 		} else {
364*0c0d06caSMauro Carvalho Chehab 			ftype = UVC_VS_FRAME_FRAME_BASED;
365*0c0d06caSMauro Carvalho Chehab 			if (buffer[27])
366*0c0d06caSMauro Carvalho Chehab 				format->flags = UVC_FMT_FLAG_COMPRESSED;
367*0c0d06caSMauro Carvalho Chehab 		}
368*0c0d06caSMauro Carvalho Chehab 		break;
369*0c0d06caSMauro Carvalho Chehab 
370*0c0d06caSMauro Carvalho Chehab 	case UVC_VS_FORMAT_MJPEG:
371*0c0d06caSMauro Carvalho Chehab 		if (buflen < 11) {
372*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
373*0c0d06caSMauro Carvalho Chehab 			       "interface %d FORMAT error\n",
374*0c0d06caSMauro Carvalho Chehab 			       dev->udev->devnum,
375*0c0d06caSMauro Carvalho Chehab 			       alts->desc.bInterfaceNumber);
376*0c0d06caSMauro Carvalho Chehab 			return -EINVAL;
377*0c0d06caSMauro Carvalho Chehab 		}
378*0c0d06caSMauro Carvalho Chehab 
379*0c0d06caSMauro Carvalho Chehab 		strlcpy(format->name, "MJPEG", sizeof format->name);
380*0c0d06caSMauro Carvalho Chehab 		format->fcc = V4L2_PIX_FMT_MJPEG;
381*0c0d06caSMauro Carvalho Chehab 		format->flags = UVC_FMT_FLAG_COMPRESSED;
382*0c0d06caSMauro Carvalho Chehab 		format->bpp = 0;
383*0c0d06caSMauro Carvalho Chehab 		ftype = UVC_VS_FRAME_MJPEG;
384*0c0d06caSMauro Carvalho Chehab 		break;
385*0c0d06caSMauro Carvalho Chehab 
386*0c0d06caSMauro Carvalho Chehab 	case UVC_VS_FORMAT_DV:
387*0c0d06caSMauro Carvalho Chehab 		if (buflen < 9) {
388*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
389*0c0d06caSMauro Carvalho Chehab 			       "interface %d FORMAT error\n",
390*0c0d06caSMauro Carvalho Chehab 			       dev->udev->devnum,
391*0c0d06caSMauro Carvalho Chehab 			       alts->desc.bInterfaceNumber);
392*0c0d06caSMauro Carvalho Chehab 			return -EINVAL;
393*0c0d06caSMauro Carvalho Chehab 		}
394*0c0d06caSMauro Carvalho Chehab 
395*0c0d06caSMauro Carvalho Chehab 		switch (buffer[8] & 0x7f) {
396*0c0d06caSMauro Carvalho Chehab 		case 0:
397*0c0d06caSMauro Carvalho Chehab 			strlcpy(format->name, "SD-DV", sizeof format->name);
398*0c0d06caSMauro Carvalho Chehab 			break;
399*0c0d06caSMauro Carvalho Chehab 		case 1:
400*0c0d06caSMauro Carvalho Chehab 			strlcpy(format->name, "SDL-DV", sizeof format->name);
401*0c0d06caSMauro Carvalho Chehab 			break;
402*0c0d06caSMauro Carvalho Chehab 		case 2:
403*0c0d06caSMauro Carvalho Chehab 			strlcpy(format->name, "HD-DV", sizeof format->name);
404*0c0d06caSMauro Carvalho Chehab 			break;
405*0c0d06caSMauro Carvalho Chehab 		default:
406*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
407*0c0d06caSMauro Carvalho Chehab 			       "interface %d: unknown DV format %u\n",
408*0c0d06caSMauro Carvalho Chehab 			       dev->udev->devnum,
409*0c0d06caSMauro Carvalho Chehab 			       alts->desc.bInterfaceNumber, buffer[8]);
410*0c0d06caSMauro Carvalho Chehab 			return -EINVAL;
411*0c0d06caSMauro Carvalho Chehab 		}
412*0c0d06caSMauro Carvalho Chehab 
413*0c0d06caSMauro Carvalho Chehab 		strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz",
414*0c0d06caSMauro Carvalho Chehab 			sizeof format->name);
415*0c0d06caSMauro Carvalho Chehab 
416*0c0d06caSMauro Carvalho Chehab 		format->fcc = V4L2_PIX_FMT_DV;
417*0c0d06caSMauro Carvalho Chehab 		format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM;
418*0c0d06caSMauro Carvalho Chehab 		format->bpp = 0;
419*0c0d06caSMauro Carvalho Chehab 		ftype = 0;
420*0c0d06caSMauro Carvalho Chehab 
421*0c0d06caSMauro Carvalho Chehab 		/* Create a dummy frame descriptor. */
422*0c0d06caSMauro Carvalho Chehab 		frame = &format->frame[0];
423*0c0d06caSMauro Carvalho Chehab 		memset(&format->frame[0], 0, sizeof format->frame[0]);
424*0c0d06caSMauro Carvalho Chehab 		frame->bFrameIntervalType = 1;
425*0c0d06caSMauro Carvalho Chehab 		frame->dwDefaultFrameInterval = 1;
426*0c0d06caSMauro Carvalho Chehab 		frame->dwFrameInterval = *intervals;
427*0c0d06caSMauro Carvalho Chehab 		*(*intervals)++ = 1;
428*0c0d06caSMauro Carvalho Chehab 		format->nframes = 1;
429*0c0d06caSMauro Carvalho Chehab 		break;
430*0c0d06caSMauro Carvalho Chehab 
431*0c0d06caSMauro Carvalho Chehab 	case UVC_VS_FORMAT_MPEG2TS:
432*0c0d06caSMauro Carvalho Chehab 	case UVC_VS_FORMAT_STREAM_BASED:
433*0c0d06caSMauro Carvalho Chehab 		/* Not supported yet. */
434*0c0d06caSMauro Carvalho Chehab 	default:
435*0c0d06caSMauro Carvalho Chehab 		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
436*0c0d06caSMauro Carvalho Chehab 		       "interface %d unsupported format %u\n",
437*0c0d06caSMauro Carvalho Chehab 		       dev->udev->devnum, alts->desc.bInterfaceNumber,
438*0c0d06caSMauro Carvalho Chehab 		       buffer[2]);
439*0c0d06caSMauro Carvalho Chehab 		return -EINVAL;
440*0c0d06caSMauro Carvalho Chehab 	}
441*0c0d06caSMauro Carvalho Chehab 
442*0c0d06caSMauro Carvalho Chehab 	uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name);
443*0c0d06caSMauro Carvalho Chehab 
444*0c0d06caSMauro Carvalho Chehab 	buflen -= buffer[0];
445*0c0d06caSMauro Carvalho Chehab 	buffer += buffer[0];
446*0c0d06caSMauro Carvalho Chehab 
447*0c0d06caSMauro Carvalho Chehab 	/* Parse the frame descriptors. Only uncompressed, MJPEG and frame
448*0c0d06caSMauro Carvalho Chehab 	 * based formats have frame descriptors.
449*0c0d06caSMauro Carvalho Chehab 	 */
450*0c0d06caSMauro Carvalho Chehab 	while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
451*0c0d06caSMauro Carvalho Chehab 	       buffer[2] == ftype) {
452*0c0d06caSMauro Carvalho Chehab 		frame = &format->frame[format->nframes];
453*0c0d06caSMauro Carvalho Chehab 		if (ftype != UVC_VS_FRAME_FRAME_BASED)
454*0c0d06caSMauro Carvalho Chehab 			n = buflen > 25 ? buffer[25] : 0;
455*0c0d06caSMauro Carvalho Chehab 		else
456*0c0d06caSMauro Carvalho Chehab 			n = buflen > 21 ? buffer[21] : 0;
457*0c0d06caSMauro Carvalho Chehab 
458*0c0d06caSMauro Carvalho Chehab 		n = n ? n : 3;
459*0c0d06caSMauro Carvalho Chehab 
460*0c0d06caSMauro Carvalho Chehab 		if (buflen < 26 + 4*n) {
461*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
462*0c0d06caSMauro Carvalho Chehab 			       "interface %d FRAME error\n", dev->udev->devnum,
463*0c0d06caSMauro Carvalho Chehab 			       alts->desc.bInterfaceNumber);
464*0c0d06caSMauro Carvalho Chehab 			return -EINVAL;
465*0c0d06caSMauro Carvalho Chehab 		}
466*0c0d06caSMauro Carvalho Chehab 
467*0c0d06caSMauro Carvalho Chehab 		frame->bFrameIndex = buffer[3];
468*0c0d06caSMauro Carvalho Chehab 		frame->bmCapabilities = buffer[4];
469*0c0d06caSMauro Carvalho Chehab 		frame->wWidth = get_unaligned_le16(&buffer[5]);
470*0c0d06caSMauro Carvalho Chehab 		frame->wHeight = get_unaligned_le16(&buffer[7]);
471*0c0d06caSMauro Carvalho Chehab 		frame->dwMinBitRate = get_unaligned_le32(&buffer[9]);
472*0c0d06caSMauro Carvalho Chehab 		frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]);
473*0c0d06caSMauro Carvalho Chehab 		if (ftype != UVC_VS_FRAME_FRAME_BASED) {
474*0c0d06caSMauro Carvalho Chehab 			frame->dwMaxVideoFrameBufferSize =
475*0c0d06caSMauro Carvalho Chehab 				get_unaligned_le32(&buffer[17]);
476*0c0d06caSMauro Carvalho Chehab 			frame->dwDefaultFrameInterval =
477*0c0d06caSMauro Carvalho Chehab 				get_unaligned_le32(&buffer[21]);
478*0c0d06caSMauro Carvalho Chehab 			frame->bFrameIntervalType = buffer[25];
479*0c0d06caSMauro Carvalho Chehab 		} else {
480*0c0d06caSMauro Carvalho Chehab 			frame->dwMaxVideoFrameBufferSize = 0;
481*0c0d06caSMauro Carvalho Chehab 			frame->dwDefaultFrameInterval =
482*0c0d06caSMauro Carvalho Chehab 				get_unaligned_le32(&buffer[17]);
483*0c0d06caSMauro Carvalho Chehab 			frame->bFrameIntervalType = buffer[21];
484*0c0d06caSMauro Carvalho Chehab 		}
485*0c0d06caSMauro Carvalho Chehab 		frame->dwFrameInterval = *intervals;
486*0c0d06caSMauro Carvalho Chehab 
487*0c0d06caSMauro Carvalho Chehab 		/* Several UVC chipsets screw up dwMaxVideoFrameBufferSize
488*0c0d06caSMauro Carvalho Chehab 		 * completely. Observed behaviours range from setting the
489*0c0d06caSMauro Carvalho Chehab 		 * value to 1.1x the actual frame size to hardwiring the
490*0c0d06caSMauro Carvalho Chehab 		 * 16 low bits to 0. This results in a higher than necessary
491*0c0d06caSMauro Carvalho Chehab 		 * memory usage as well as a wrong image size information. For
492*0c0d06caSMauro Carvalho Chehab 		 * uncompressed formats this can be fixed by computing the
493*0c0d06caSMauro Carvalho Chehab 		 * value from the frame size.
494*0c0d06caSMauro Carvalho Chehab 		 */
495*0c0d06caSMauro Carvalho Chehab 		if (!(format->flags & UVC_FMT_FLAG_COMPRESSED))
496*0c0d06caSMauro Carvalho Chehab 			frame->dwMaxVideoFrameBufferSize = format->bpp
497*0c0d06caSMauro Carvalho Chehab 				* frame->wWidth * frame->wHeight / 8;
498*0c0d06caSMauro Carvalho Chehab 
499*0c0d06caSMauro Carvalho Chehab 		/* Some bogus devices report dwMinFrameInterval equal to
500*0c0d06caSMauro Carvalho Chehab 		 * dwMaxFrameInterval and have dwFrameIntervalStep set to
501*0c0d06caSMauro Carvalho Chehab 		 * zero. Setting all null intervals to 1 fixes the problem and
502*0c0d06caSMauro Carvalho Chehab 		 * some other divisions by zero that could happen.
503*0c0d06caSMauro Carvalho Chehab 		 */
504*0c0d06caSMauro Carvalho Chehab 		for (i = 0; i < n; ++i) {
505*0c0d06caSMauro Carvalho Chehab 			interval = get_unaligned_le32(&buffer[26+4*i]);
506*0c0d06caSMauro Carvalho Chehab 			*(*intervals)++ = interval ? interval : 1;
507*0c0d06caSMauro Carvalho Chehab 		}
508*0c0d06caSMauro Carvalho Chehab 
509*0c0d06caSMauro Carvalho Chehab 		/* Make sure that the default frame interval stays between
510*0c0d06caSMauro Carvalho Chehab 		 * the boundaries.
511*0c0d06caSMauro Carvalho Chehab 		 */
512*0c0d06caSMauro Carvalho Chehab 		n -= frame->bFrameIntervalType ? 1 : 2;
513*0c0d06caSMauro Carvalho Chehab 		frame->dwDefaultFrameInterval =
514*0c0d06caSMauro Carvalho Chehab 			min(frame->dwFrameInterval[n],
515*0c0d06caSMauro Carvalho Chehab 			    max(frame->dwFrameInterval[0],
516*0c0d06caSMauro Carvalho Chehab 				frame->dwDefaultFrameInterval));
517*0c0d06caSMauro Carvalho Chehab 
518*0c0d06caSMauro Carvalho Chehab 		if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) {
519*0c0d06caSMauro Carvalho Chehab 			frame->bFrameIntervalType = 1;
520*0c0d06caSMauro Carvalho Chehab 			frame->dwFrameInterval[0] =
521*0c0d06caSMauro Carvalho Chehab 				frame->dwDefaultFrameInterval;
522*0c0d06caSMauro Carvalho Chehab 		}
523*0c0d06caSMauro Carvalho Chehab 
524*0c0d06caSMauro Carvalho Chehab 		uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n",
525*0c0d06caSMauro Carvalho Chehab 			frame->wWidth, frame->wHeight,
526*0c0d06caSMauro Carvalho Chehab 			10000000/frame->dwDefaultFrameInterval,
527*0c0d06caSMauro Carvalho Chehab 			(100000000/frame->dwDefaultFrameInterval)%10);
528*0c0d06caSMauro Carvalho Chehab 
529*0c0d06caSMauro Carvalho Chehab 		format->nframes++;
530*0c0d06caSMauro Carvalho Chehab 		buflen -= buffer[0];
531*0c0d06caSMauro Carvalho Chehab 		buffer += buffer[0];
532*0c0d06caSMauro Carvalho Chehab 	}
533*0c0d06caSMauro Carvalho Chehab 
534*0c0d06caSMauro Carvalho Chehab 	if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
535*0c0d06caSMauro Carvalho Chehab 	    buffer[2] == UVC_VS_STILL_IMAGE_FRAME) {
536*0c0d06caSMauro Carvalho Chehab 		buflen -= buffer[0];
537*0c0d06caSMauro Carvalho Chehab 		buffer += buffer[0];
538*0c0d06caSMauro Carvalho Chehab 	}
539*0c0d06caSMauro Carvalho Chehab 
540*0c0d06caSMauro Carvalho Chehab 	if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
541*0c0d06caSMauro Carvalho Chehab 	    buffer[2] == UVC_VS_COLORFORMAT) {
542*0c0d06caSMauro Carvalho Chehab 		if (buflen < 6) {
543*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
544*0c0d06caSMauro Carvalho Chehab 			       "interface %d COLORFORMAT error\n",
545*0c0d06caSMauro Carvalho Chehab 			       dev->udev->devnum,
546*0c0d06caSMauro Carvalho Chehab 			       alts->desc.bInterfaceNumber);
547*0c0d06caSMauro Carvalho Chehab 			return -EINVAL;
548*0c0d06caSMauro Carvalho Chehab 		}
549*0c0d06caSMauro Carvalho Chehab 
550*0c0d06caSMauro Carvalho Chehab 		format->colorspace = uvc_colorspace(buffer[3]);
551*0c0d06caSMauro Carvalho Chehab 
552*0c0d06caSMauro Carvalho Chehab 		buflen -= buffer[0];
553*0c0d06caSMauro Carvalho Chehab 		buffer += buffer[0];
554*0c0d06caSMauro Carvalho Chehab 	}
555*0c0d06caSMauro Carvalho Chehab 
556*0c0d06caSMauro Carvalho Chehab 	return buffer - start;
557*0c0d06caSMauro Carvalho Chehab }
558*0c0d06caSMauro Carvalho Chehab 
559*0c0d06caSMauro Carvalho Chehab static int uvc_parse_streaming(struct uvc_device *dev,
560*0c0d06caSMauro Carvalho Chehab 	struct usb_interface *intf)
561*0c0d06caSMauro Carvalho Chehab {
562*0c0d06caSMauro Carvalho Chehab 	struct uvc_streaming *streaming = NULL;
563*0c0d06caSMauro Carvalho Chehab 	struct uvc_format *format;
564*0c0d06caSMauro Carvalho Chehab 	struct uvc_frame *frame;
565*0c0d06caSMauro Carvalho Chehab 	struct usb_host_interface *alts = &intf->altsetting[0];
566*0c0d06caSMauro Carvalho Chehab 	unsigned char *_buffer, *buffer = alts->extra;
567*0c0d06caSMauro Carvalho Chehab 	int _buflen, buflen = alts->extralen;
568*0c0d06caSMauro Carvalho Chehab 	unsigned int nformats = 0, nframes = 0, nintervals = 0;
569*0c0d06caSMauro Carvalho Chehab 	unsigned int size, i, n, p;
570*0c0d06caSMauro Carvalho Chehab 	__u32 *interval;
571*0c0d06caSMauro Carvalho Chehab 	__u16 psize;
572*0c0d06caSMauro Carvalho Chehab 	int ret = -EINVAL;
573*0c0d06caSMauro Carvalho Chehab 
574*0c0d06caSMauro Carvalho Chehab 	if (intf->cur_altsetting->desc.bInterfaceSubClass
575*0c0d06caSMauro Carvalho Chehab 		!= UVC_SC_VIDEOSTREAMING) {
576*0c0d06caSMauro Carvalho Chehab 		uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a "
577*0c0d06caSMauro Carvalho Chehab 			"video streaming interface\n", dev->udev->devnum,
578*0c0d06caSMauro Carvalho Chehab 			intf->altsetting[0].desc.bInterfaceNumber);
579*0c0d06caSMauro Carvalho Chehab 		return -EINVAL;
580*0c0d06caSMauro Carvalho Chehab 	}
581*0c0d06caSMauro Carvalho Chehab 
582*0c0d06caSMauro Carvalho Chehab 	if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) {
583*0c0d06caSMauro Carvalho Chehab 		uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already "
584*0c0d06caSMauro Carvalho Chehab 			"claimed\n", dev->udev->devnum,
585*0c0d06caSMauro Carvalho Chehab 			intf->altsetting[0].desc.bInterfaceNumber);
586*0c0d06caSMauro Carvalho Chehab 		return -EINVAL;
587*0c0d06caSMauro Carvalho Chehab 	}
588*0c0d06caSMauro Carvalho Chehab 
589*0c0d06caSMauro Carvalho Chehab 	streaming = kzalloc(sizeof *streaming, GFP_KERNEL);
590*0c0d06caSMauro Carvalho Chehab 	if (streaming == NULL) {
591*0c0d06caSMauro Carvalho Chehab 		usb_driver_release_interface(&uvc_driver.driver, intf);
592*0c0d06caSMauro Carvalho Chehab 		return -EINVAL;
593*0c0d06caSMauro Carvalho Chehab 	}
594*0c0d06caSMauro Carvalho Chehab 
595*0c0d06caSMauro Carvalho Chehab 	mutex_init(&streaming->mutex);
596*0c0d06caSMauro Carvalho Chehab 	streaming->dev = dev;
597*0c0d06caSMauro Carvalho Chehab 	streaming->intf = usb_get_intf(intf);
598*0c0d06caSMauro Carvalho Chehab 	streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
599*0c0d06caSMauro Carvalho Chehab 
600*0c0d06caSMauro Carvalho Chehab 	/* The Pico iMage webcam has its class-specific interface descriptors
601*0c0d06caSMauro Carvalho Chehab 	 * after the endpoint descriptors.
602*0c0d06caSMauro Carvalho Chehab 	 */
603*0c0d06caSMauro Carvalho Chehab 	if (buflen == 0) {
604*0c0d06caSMauro Carvalho Chehab 		for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
605*0c0d06caSMauro Carvalho Chehab 			struct usb_host_endpoint *ep = &alts->endpoint[i];
606*0c0d06caSMauro Carvalho Chehab 
607*0c0d06caSMauro Carvalho Chehab 			if (ep->extralen == 0)
608*0c0d06caSMauro Carvalho Chehab 				continue;
609*0c0d06caSMauro Carvalho Chehab 
610*0c0d06caSMauro Carvalho Chehab 			if (ep->extralen > 2 &&
611*0c0d06caSMauro Carvalho Chehab 			    ep->extra[1] == USB_DT_CS_INTERFACE) {
612*0c0d06caSMauro Carvalho Chehab 				uvc_trace(UVC_TRACE_DESCR, "trying extra data "
613*0c0d06caSMauro Carvalho Chehab 					"from endpoint %u.\n", i);
614*0c0d06caSMauro Carvalho Chehab 				buffer = alts->endpoint[i].extra;
615*0c0d06caSMauro Carvalho Chehab 				buflen = alts->endpoint[i].extralen;
616*0c0d06caSMauro Carvalho Chehab 				break;
617*0c0d06caSMauro Carvalho Chehab 			}
618*0c0d06caSMauro Carvalho Chehab 		}
619*0c0d06caSMauro Carvalho Chehab 	}
620*0c0d06caSMauro Carvalho Chehab 
621*0c0d06caSMauro Carvalho Chehab 	/* Skip the standard interface descriptors. */
622*0c0d06caSMauro Carvalho Chehab 	while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) {
623*0c0d06caSMauro Carvalho Chehab 		buflen -= buffer[0];
624*0c0d06caSMauro Carvalho Chehab 		buffer += buffer[0];
625*0c0d06caSMauro Carvalho Chehab 	}
626*0c0d06caSMauro Carvalho Chehab 
627*0c0d06caSMauro Carvalho Chehab 	if (buflen <= 2) {
628*0c0d06caSMauro Carvalho Chehab 		uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming "
629*0c0d06caSMauro Carvalho Chehab 			"interface descriptors found.\n");
630*0c0d06caSMauro Carvalho Chehab 		goto error;
631*0c0d06caSMauro Carvalho Chehab 	}
632*0c0d06caSMauro Carvalho Chehab 
633*0c0d06caSMauro Carvalho Chehab 	/* Parse the header descriptor. */
634*0c0d06caSMauro Carvalho Chehab 	switch (buffer[2]) {
635*0c0d06caSMauro Carvalho Chehab 	case UVC_VS_OUTPUT_HEADER:
636*0c0d06caSMauro Carvalho Chehab 		streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
637*0c0d06caSMauro Carvalho Chehab 		size = 9;
638*0c0d06caSMauro Carvalho Chehab 		break;
639*0c0d06caSMauro Carvalho Chehab 
640*0c0d06caSMauro Carvalho Chehab 	case UVC_VS_INPUT_HEADER:
641*0c0d06caSMauro Carvalho Chehab 		streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
642*0c0d06caSMauro Carvalho Chehab 		size = 13;
643*0c0d06caSMauro Carvalho Chehab 		break;
644*0c0d06caSMauro Carvalho Chehab 
645*0c0d06caSMauro Carvalho Chehab 	default:
646*0c0d06caSMauro Carvalho Chehab 		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
647*0c0d06caSMauro Carvalho Chehab 			"%d HEADER descriptor not found.\n", dev->udev->devnum,
648*0c0d06caSMauro Carvalho Chehab 			alts->desc.bInterfaceNumber);
649*0c0d06caSMauro Carvalho Chehab 		goto error;
650*0c0d06caSMauro Carvalho Chehab 	}
651*0c0d06caSMauro Carvalho Chehab 
652*0c0d06caSMauro Carvalho Chehab 	p = buflen >= 4 ? buffer[3] : 0;
653*0c0d06caSMauro Carvalho Chehab 	n = buflen >= size ? buffer[size-1] : 0;
654*0c0d06caSMauro Carvalho Chehab 
655*0c0d06caSMauro Carvalho Chehab 	if (buflen < size + p*n) {
656*0c0d06caSMauro Carvalho Chehab 		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
657*0c0d06caSMauro Carvalho Chehab 			"interface %d HEADER descriptor is invalid.\n",
658*0c0d06caSMauro Carvalho Chehab 			dev->udev->devnum, alts->desc.bInterfaceNumber);
659*0c0d06caSMauro Carvalho Chehab 		goto error;
660*0c0d06caSMauro Carvalho Chehab 	}
661*0c0d06caSMauro Carvalho Chehab 
662*0c0d06caSMauro Carvalho Chehab 	streaming->header.bNumFormats = p;
663*0c0d06caSMauro Carvalho Chehab 	streaming->header.bEndpointAddress = buffer[6];
664*0c0d06caSMauro Carvalho Chehab 	if (buffer[2] == UVC_VS_INPUT_HEADER) {
665*0c0d06caSMauro Carvalho Chehab 		streaming->header.bmInfo = buffer[7];
666*0c0d06caSMauro Carvalho Chehab 		streaming->header.bTerminalLink = buffer[8];
667*0c0d06caSMauro Carvalho Chehab 		streaming->header.bStillCaptureMethod = buffer[9];
668*0c0d06caSMauro Carvalho Chehab 		streaming->header.bTriggerSupport = buffer[10];
669*0c0d06caSMauro Carvalho Chehab 		streaming->header.bTriggerUsage = buffer[11];
670*0c0d06caSMauro Carvalho Chehab 	} else {
671*0c0d06caSMauro Carvalho Chehab 		streaming->header.bTerminalLink = buffer[7];
672*0c0d06caSMauro Carvalho Chehab 	}
673*0c0d06caSMauro Carvalho Chehab 	streaming->header.bControlSize = n;
674*0c0d06caSMauro Carvalho Chehab 
675*0c0d06caSMauro Carvalho Chehab 	streaming->header.bmaControls = kmemdup(&buffer[size], p * n,
676*0c0d06caSMauro Carvalho Chehab 						GFP_KERNEL);
677*0c0d06caSMauro Carvalho Chehab 	if (streaming->header.bmaControls == NULL) {
678*0c0d06caSMauro Carvalho Chehab 		ret = -ENOMEM;
679*0c0d06caSMauro Carvalho Chehab 		goto error;
680*0c0d06caSMauro Carvalho Chehab 	}
681*0c0d06caSMauro Carvalho Chehab 
682*0c0d06caSMauro Carvalho Chehab 	buflen -= buffer[0];
683*0c0d06caSMauro Carvalho Chehab 	buffer += buffer[0];
684*0c0d06caSMauro Carvalho Chehab 
685*0c0d06caSMauro Carvalho Chehab 	_buffer = buffer;
686*0c0d06caSMauro Carvalho Chehab 	_buflen = buflen;
687*0c0d06caSMauro Carvalho Chehab 
688*0c0d06caSMauro Carvalho Chehab 	/* Count the format and frame descriptors. */
689*0c0d06caSMauro Carvalho Chehab 	while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) {
690*0c0d06caSMauro Carvalho Chehab 		switch (_buffer[2]) {
691*0c0d06caSMauro Carvalho Chehab 		case UVC_VS_FORMAT_UNCOMPRESSED:
692*0c0d06caSMauro Carvalho Chehab 		case UVC_VS_FORMAT_MJPEG:
693*0c0d06caSMauro Carvalho Chehab 		case UVC_VS_FORMAT_FRAME_BASED:
694*0c0d06caSMauro Carvalho Chehab 			nformats++;
695*0c0d06caSMauro Carvalho Chehab 			break;
696*0c0d06caSMauro Carvalho Chehab 
697*0c0d06caSMauro Carvalho Chehab 		case UVC_VS_FORMAT_DV:
698*0c0d06caSMauro Carvalho Chehab 			/* DV format has no frame descriptor. We will create a
699*0c0d06caSMauro Carvalho Chehab 			 * dummy frame descriptor with a dummy frame interval.
700*0c0d06caSMauro Carvalho Chehab 			 */
701*0c0d06caSMauro Carvalho Chehab 			nformats++;
702*0c0d06caSMauro Carvalho Chehab 			nframes++;
703*0c0d06caSMauro Carvalho Chehab 			nintervals++;
704*0c0d06caSMauro Carvalho Chehab 			break;
705*0c0d06caSMauro Carvalho Chehab 
706*0c0d06caSMauro Carvalho Chehab 		case UVC_VS_FORMAT_MPEG2TS:
707*0c0d06caSMauro Carvalho Chehab 		case UVC_VS_FORMAT_STREAM_BASED:
708*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
709*0c0d06caSMauro Carvalho Chehab 				"interface %d FORMAT %u is not supported.\n",
710*0c0d06caSMauro Carvalho Chehab 				dev->udev->devnum,
711*0c0d06caSMauro Carvalho Chehab 				alts->desc.bInterfaceNumber, _buffer[2]);
712*0c0d06caSMauro Carvalho Chehab 			break;
713*0c0d06caSMauro Carvalho Chehab 
714*0c0d06caSMauro Carvalho Chehab 		case UVC_VS_FRAME_UNCOMPRESSED:
715*0c0d06caSMauro Carvalho Chehab 		case UVC_VS_FRAME_MJPEG:
716*0c0d06caSMauro Carvalho Chehab 			nframes++;
717*0c0d06caSMauro Carvalho Chehab 			if (_buflen > 25)
718*0c0d06caSMauro Carvalho Chehab 				nintervals += _buffer[25] ? _buffer[25] : 3;
719*0c0d06caSMauro Carvalho Chehab 			break;
720*0c0d06caSMauro Carvalho Chehab 
721*0c0d06caSMauro Carvalho Chehab 		case UVC_VS_FRAME_FRAME_BASED:
722*0c0d06caSMauro Carvalho Chehab 			nframes++;
723*0c0d06caSMauro Carvalho Chehab 			if (_buflen > 21)
724*0c0d06caSMauro Carvalho Chehab 				nintervals += _buffer[21] ? _buffer[21] : 3;
725*0c0d06caSMauro Carvalho Chehab 			break;
726*0c0d06caSMauro Carvalho Chehab 		}
727*0c0d06caSMauro Carvalho Chehab 
728*0c0d06caSMauro Carvalho Chehab 		_buflen -= _buffer[0];
729*0c0d06caSMauro Carvalho Chehab 		_buffer += _buffer[0];
730*0c0d06caSMauro Carvalho Chehab 	}
731*0c0d06caSMauro Carvalho Chehab 
732*0c0d06caSMauro Carvalho Chehab 	if (nformats == 0) {
733*0c0d06caSMauro Carvalho Chehab 		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
734*0c0d06caSMauro Carvalho Chehab 			"%d has no supported formats defined.\n",
735*0c0d06caSMauro Carvalho Chehab 			dev->udev->devnum, alts->desc.bInterfaceNumber);
736*0c0d06caSMauro Carvalho Chehab 		goto error;
737*0c0d06caSMauro Carvalho Chehab 	}
738*0c0d06caSMauro Carvalho Chehab 
739*0c0d06caSMauro Carvalho Chehab 	size = nformats * sizeof *format + nframes * sizeof *frame
740*0c0d06caSMauro Carvalho Chehab 	     + nintervals * sizeof *interval;
741*0c0d06caSMauro Carvalho Chehab 	format = kzalloc(size, GFP_KERNEL);
742*0c0d06caSMauro Carvalho Chehab 	if (format == NULL) {
743*0c0d06caSMauro Carvalho Chehab 		ret = -ENOMEM;
744*0c0d06caSMauro Carvalho Chehab 		goto error;
745*0c0d06caSMauro Carvalho Chehab 	}
746*0c0d06caSMauro Carvalho Chehab 
747*0c0d06caSMauro Carvalho Chehab 	frame = (struct uvc_frame *)&format[nformats];
748*0c0d06caSMauro Carvalho Chehab 	interval = (__u32 *)&frame[nframes];
749*0c0d06caSMauro Carvalho Chehab 
750*0c0d06caSMauro Carvalho Chehab 	streaming->format = format;
751*0c0d06caSMauro Carvalho Chehab 	streaming->nformats = nformats;
752*0c0d06caSMauro Carvalho Chehab 
753*0c0d06caSMauro Carvalho Chehab 	/* Parse the format descriptors. */
754*0c0d06caSMauro Carvalho Chehab 	while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) {
755*0c0d06caSMauro Carvalho Chehab 		switch (buffer[2]) {
756*0c0d06caSMauro Carvalho Chehab 		case UVC_VS_FORMAT_UNCOMPRESSED:
757*0c0d06caSMauro Carvalho Chehab 		case UVC_VS_FORMAT_MJPEG:
758*0c0d06caSMauro Carvalho Chehab 		case UVC_VS_FORMAT_DV:
759*0c0d06caSMauro Carvalho Chehab 		case UVC_VS_FORMAT_FRAME_BASED:
760*0c0d06caSMauro Carvalho Chehab 			format->frame = frame;
761*0c0d06caSMauro Carvalho Chehab 			ret = uvc_parse_format(dev, streaming, format,
762*0c0d06caSMauro Carvalho Chehab 				&interval, buffer, buflen);
763*0c0d06caSMauro Carvalho Chehab 			if (ret < 0)
764*0c0d06caSMauro Carvalho Chehab 				goto error;
765*0c0d06caSMauro Carvalho Chehab 
766*0c0d06caSMauro Carvalho Chehab 			frame += format->nframes;
767*0c0d06caSMauro Carvalho Chehab 			format++;
768*0c0d06caSMauro Carvalho Chehab 
769*0c0d06caSMauro Carvalho Chehab 			buflen -= ret;
770*0c0d06caSMauro Carvalho Chehab 			buffer += ret;
771*0c0d06caSMauro Carvalho Chehab 			continue;
772*0c0d06caSMauro Carvalho Chehab 
773*0c0d06caSMauro Carvalho Chehab 		default:
774*0c0d06caSMauro Carvalho Chehab 			break;
775*0c0d06caSMauro Carvalho Chehab 		}
776*0c0d06caSMauro Carvalho Chehab 
777*0c0d06caSMauro Carvalho Chehab 		buflen -= buffer[0];
778*0c0d06caSMauro Carvalho Chehab 		buffer += buffer[0];
779*0c0d06caSMauro Carvalho Chehab 	}
780*0c0d06caSMauro Carvalho Chehab 
781*0c0d06caSMauro Carvalho Chehab 	if (buflen)
782*0c0d06caSMauro Carvalho Chehab 		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
783*0c0d06caSMauro Carvalho Chehab 			"%d has %u bytes of trailing descriptor garbage.\n",
784*0c0d06caSMauro Carvalho Chehab 			dev->udev->devnum, alts->desc.bInterfaceNumber, buflen);
785*0c0d06caSMauro Carvalho Chehab 
786*0c0d06caSMauro Carvalho Chehab 	/* Parse the alternate settings to find the maximum bandwidth. */
787*0c0d06caSMauro Carvalho Chehab 	for (i = 0; i < intf->num_altsetting; ++i) {
788*0c0d06caSMauro Carvalho Chehab 		struct usb_host_endpoint *ep;
789*0c0d06caSMauro Carvalho Chehab 		alts = &intf->altsetting[i];
790*0c0d06caSMauro Carvalho Chehab 		ep = uvc_find_endpoint(alts,
791*0c0d06caSMauro Carvalho Chehab 				streaming->header.bEndpointAddress);
792*0c0d06caSMauro Carvalho Chehab 		if (ep == NULL)
793*0c0d06caSMauro Carvalho Chehab 			continue;
794*0c0d06caSMauro Carvalho Chehab 
795*0c0d06caSMauro Carvalho Chehab 		psize = le16_to_cpu(ep->desc.wMaxPacketSize);
796*0c0d06caSMauro Carvalho Chehab 		psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
797*0c0d06caSMauro Carvalho Chehab 		if (psize > streaming->maxpsize)
798*0c0d06caSMauro Carvalho Chehab 			streaming->maxpsize = psize;
799*0c0d06caSMauro Carvalho Chehab 	}
800*0c0d06caSMauro Carvalho Chehab 
801*0c0d06caSMauro Carvalho Chehab 	list_add_tail(&streaming->list, &dev->streams);
802*0c0d06caSMauro Carvalho Chehab 	return 0;
803*0c0d06caSMauro Carvalho Chehab 
804*0c0d06caSMauro Carvalho Chehab error:
805*0c0d06caSMauro Carvalho Chehab 	usb_driver_release_interface(&uvc_driver.driver, intf);
806*0c0d06caSMauro Carvalho Chehab 	usb_put_intf(intf);
807*0c0d06caSMauro Carvalho Chehab 	kfree(streaming->format);
808*0c0d06caSMauro Carvalho Chehab 	kfree(streaming->header.bmaControls);
809*0c0d06caSMauro Carvalho Chehab 	kfree(streaming);
810*0c0d06caSMauro Carvalho Chehab 	return ret;
811*0c0d06caSMauro Carvalho Chehab }
812*0c0d06caSMauro Carvalho Chehab 
813*0c0d06caSMauro Carvalho Chehab static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id,
814*0c0d06caSMauro Carvalho Chehab 		unsigned int num_pads, unsigned int extra_size)
815*0c0d06caSMauro Carvalho Chehab {
816*0c0d06caSMauro Carvalho Chehab 	struct uvc_entity *entity;
817*0c0d06caSMauro Carvalho Chehab 	unsigned int num_inputs;
818*0c0d06caSMauro Carvalho Chehab 	unsigned int size;
819*0c0d06caSMauro Carvalho Chehab 	unsigned int i;
820*0c0d06caSMauro Carvalho Chehab 
821*0c0d06caSMauro Carvalho Chehab 	extra_size = ALIGN(extra_size, sizeof(*entity->pads));
822*0c0d06caSMauro Carvalho Chehab 	num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1;
823*0c0d06caSMauro Carvalho Chehab 	size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads
824*0c0d06caSMauro Carvalho Chehab 	     + num_inputs;
825*0c0d06caSMauro Carvalho Chehab 	entity = kzalloc(size, GFP_KERNEL);
826*0c0d06caSMauro Carvalho Chehab 	if (entity == NULL)
827*0c0d06caSMauro Carvalho Chehab 		return NULL;
828*0c0d06caSMauro Carvalho Chehab 
829*0c0d06caSMauro Carvalho Chehab 	entity->id = id;
830*0c0d06caSMauro Carvalho Chehab 	entity->type = type;
831*0c0d06caSMauro Carvalho Chehab 
832*0c0d06caSMauro Carvalho Chehab 	entity->num_links = 0;
833*0c0d06caSMauro Carvalho Chehab 	entity->num_pads = num_pads;
834*0c0d06caSMauro Carvalho Chehab 	entity->pads = ((void *)(entity + 1)) + extra_size;
835*0c0d06caSMauro Carvalho Chehab 
836*0c0d06caSMauro Carvalho Chehab 	for (i = 0; i < num_inputs; ++i)
837*0c0d06caSMauro Carvalho Chehab 		entity->pads[i].flags = MEDIA_PAD_FL_SINK;
838*0c0d06caSMauro Carvalho Chehab 	if (!UVC_ENTITY_IS_OTERM(entity))
839*0c0d06caSMauro Carvalho Chehab 		entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE;
840*0c0d06caSMauro Carvalho Chehab 
841*0c0d06caSMauro Carvalho Chehab 	entity->bNrInPins = num_inputs;
842*0c0d06caSMauro Carvalho Chehab 	entity->baSourceID = (__u8 *)(&entity->pads[num_pads]);
843*0c0d06caSMauro Carvalho Chehab 
844*0c0d06caSMauro Carvalho Chehab 	return entity;
845*0c0d06caSMauro Carvalho Chehab }
846*0c0d06caSMauro Carvalho Chehab 
847*0c0d06caSMauro Carvalho Chehab /* Parse vendor-specific extensions. */
848*0c0d06caSMauro Carvalho Chehab static int uvc_parse_vendor_control(struct uvc_device *dev,
849*0c0d06caSMauro Carvalho Chehab 	const unsigned char *buffer, int buflen)
850*0c0d06caSMauro Carvalho Chehab {
851*0c0d06caSMauro Carvalho Chehab 	struct usb_device *udev = dev->udev;
852*0c0d06caSMauro Carvalho Chehab 	struct usb_host_interface *alts = dev->intf->cur_altsetting;
853*0c0d06caSMauro Carvalho Chehab 	struct uvc_entity *unit;
854*0c0d06caSMauro Carvalho Chehab 	unsigned int n, p;
855*0c0d06caSMauro Carvalho Chehab 	int handled = 0;
856*0c0d06caSMauro Carvalho Chehab 
857*0c0d06caSMauro Carvalho Chehab 	switch (le16_to_cpu(dev->udev->descriptor.idVendor)) {
858*0c0d06caSMauro Carvalho Chehab 	case 0x046d:		/* Logitech */
859*0c0d06caSMauro Carvalho Chehab 		if (buffer[1] != 0x41 || buffer[2] != 0x01)
860*0c0d06caSMauro Carvalho Chehab 			break;
861*0c0d06caSMauro Carvalho Chehab 
862*0c0d06caSMauro Carvalho Chehab 		/* Logitech implements several vendor specific functions
863*0c0d06caSMauro Carvalho Chehab 		 * through vendor specific extension units (LXU).
864*0c0d06caSMauro Carvalho Chehab 		 *
865*0c0d06caSMauro Carvalho Chehab 		 * The LXU descriptors are similar to XU descriptors
866*0c0d06caSMauro Carvalho Chehab 		 * (see "USB Device Video Class for Video Devices", section
867*0c0d06caSMauro Carvalho Chehab 		 * 3.7.2.6 "Extension Unit Descriptor") with the following
868*0c0d06caSMauro Carvalho Chehab 		 * differences:
869*0c0d06caSMauro Carvalho Chehab 		 *
870*0c0d06caSMauro Carvalho Chehab 		 * ----------------------------------------------------------
871*0c0d06caSMauro Carvalho Chehab 		 * 0		bLength		1	 Number
872*0c0d06caSMauro Carvalho Chehab 		 *	Size of this descriptor, in bytes: 24+p+n*2
873*0c0d06caSMauro Carvalho Chehab 		 * ----------------------------------------------------------
874*0c0d06caSMauro Carvalho Chehab 		 * 23+p+n	bmControlsType	N	Bitmap
875*0c0d06caSMauro Carvalho Chehab 		 * 	Individual bits in the set are defined:
876*0c0d06caSMauro Carvalho Chehab 		 * 	0: Absolute
877*0c0d06caSMauro Carvalho Chehab 		 * 	1: Relative
878*0c0d06caSMauro Carvalho Chehab 		 *
879*0c0d06caSMauro Carvalho Chehab 		 * 	This bitset is mapped exactly the same as bmControls.
880*0c0d06caSMauro Carvalho Chehab 		 * ----------------------------------------------------------
881*0c0d06caSMauro Carvalho Chehab 		 * 23+p+n*2	bReserved	1	Boolean
882*0c0d06caSMauro Carvalho Chehab 		 * ----------------------------------------------------------
883*0c0d06caSMauro Carvalho Chehab 		 * 24+p+n*2	iExtension	1	Index
884*0c0d06caSMauro Carvalho Chehab 		 *	Index of a string descriptor that describes this
885*0c0d06caSMauro Carvalho Chehab 		 *	extension unit.
886*0c0d06caSMauro Carvalho Chehab 		 * ----------------------------------------------------------
887*0c0d06caSMauro Carvalho Chehab 		 */
888*0c0d06caSMauro Carvalho Chehab 		p = buflen >= 22 ? buffer[21] : 0;
889*0c0d06caSMauro Carvalho Chehab 		n = buflen >= 25 + p ? buffer[22+p] : 0;
890*0c0d06caSMauro Carvalho Chehab 
891*0c0d06caSMauro Carvalho Chehab 		if (buflen < 25 + p + 2*n) {
892*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
893*0c0d06caSMauro Carvalho Chehab 				"interface %d EXTENSION_UNIT error\n",
894*0c0d06caSMauro Carvalho Chehab 				udev->devnum, alts->desc.bInterfaceNumber);
895*0c0d06caSMauro Carvalho Chehab 			break;
896*0c0d06caSMauro Carvalho Chehab 		}
897*0c0d06caSMauro Carvalho Chehab 
898*0c0d06caSMauro Carvalho Chehab 		unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3],
899*0c0d06caSMauro Carvalho Chehab 					p + 1, 2*n);
900*0c0d06caSMauro Carvalho Chehab 		if (unit == NULL)
901*0c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
902*0c0d06caSMauro Carvalho Chehab 
903*0c0d06caSMauro Carvalho Chehab 		memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
904*0c0d06caSMauro Carvalho Chehab 		unit->extension.bNumControls = buffer[20];
905*0c0d06caSMauro Carvalho Chehab 		memcpy(unit->baSourceID, &buffer[22], p);
906*0c0d06caSMauro Carvalho Chehab 		unit->extension.bControlSize = buffer[22+p];
907*0c0d06caSMauro Carvalho Chehab 		unit->extension.bmControls = (__u8 *)unit + sizeof(*unit);
908*0c0d06caSMauro Carvalho Chehab 		unit->extension.bmControlsType = (__u8 *)unit + sizeof(*unit)
909*0c0d06caSMauro Carvalho Chehab 					       + n;
910*0c0d06caSMauro Carvalho Chehab 		memcpy(unit->extension.bmControls, &buffer[23+p], 2*n);
911*0c0d06caSMauro Carvalho Chehab 
912*0c0d06caSMauro Carvalho Chehab 		if (buffer[24+p+2*n] != 0)
913*0c0d06caSMauro Carvalho Chehab 			usb_string(udev, buffer[24+p+2*n], unit->name,
914*0c0d06caSMauro Carvalho Chehab 				   sizeof unit->name);
915*0c0d06caSMauro Carvalho Chehab 		else
916*0c0d06caSMauro Carvalho Chehab 			sprintf(unit->name, "Extension %u", buffer[3]);
917*0c0d06caSMauro Carvalho Chehab 
918*0c0d06caSMauro Carvalho Chehab 		list_add_tail(&unit->list, &dev->entities);
919*0c0d06caSMauro Carvalho Chehab 		handled = 1;
920*0c0d06caSMauro Carvalho Chehab 		break;
921*0c0d06caSMauro Carvalho Chehab 	}
922*0c0d06caSMauro Carvalho Chehab 
923*0c0d06caSMauro Carvalho Chehab 	return handled;
924*0c0d06caSMauro Carvalho Chehab }
925*0c0d06caSMauro Carvalho Chehab 
926*0c0d06caSMauro Carvalho Chehab static int uvc_parse_standard_control(struct uvc_device *dev,
927*0c0d06caSMauro Carvalho Chehab 	const unsigned char *buffer, int buflen)
928*0c0d06caSMauro Carvalho Chehab {
929*0c0d06caSMauro Carvalho Chehab 	struct usb_device *udev = dev->udev;
930*0c0d06caSMauro Carvalho Chehab 	struct uvc_entity *unit, *term;
931*0c0d06caSMauro Carvalho Chehab 	struct usb_interface *intf;
932*0c0d06caSMauro Carvalho Chehab 	struct usb_host_interface *alts = dev->intf->cur_altsetting;
933*0c0d06caSMauro Carvalho Chehab 	unsigned int i, n, p, len;
934*0c0d06caSMauro Carvalho Chehab 	__u16 type;
935*0c0d06caSMauro Carvalho Chehab 
936*0c0d06caSMauro Carvalho Chehab 	switch (buffer[2]) {
937*0c0d06caSMauro Carvalho Chehab 	case UVC_VC_HEADER:
938*0c0d06caSMauro Carvalho Chehab 		n = buflen >= 12 ? buffer[11] : 0;
939*0c0d06caSMauro Carvalho Chehab 
940*0c0d06caSMauro Carvalho Chehab 		if (buflen < 12 || buflen < 12 + n) {
941*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
942*0c0d06caSMauro Carvalho Chehab 				"interface %d HEADER error\n", udev->devnum,
943*0c0d06caSMauro Carvalho Chehab 				alts->desc.bInterfaceNumber);
944*0c0d06caSMauro Carvalho Chehab 			return -EINVAL;
945*0c0d06caSMauro Carvalho Chehab 		}
946*0c0d06caSMauro Carvalho Chehab 
947*0c0d06caSMauro Carvalho Chehab 		dev->uvc_version = get_unaligned_le16(&buffer[3]);
948*0c0d06caSMauro Carvalho Chehab 		dev->clock_frequency = get_unaligned_le32(&buffer[7]);
949*0c0d06caSMauro Carvalho Chehab 
950*0c0d06caSMauro Carvalho Chehab 		/* Parse all USB Video Streaming interfaces. */
951*0c0d06caSMauro Carvalho Chehab 		for (i = 0; i < n; ++i) {
952*0c0d06caSMauro Carvalho Chehab 			intf = usb_ifnum_to_if(udev, buffer[12+i]);
953*0c0d06caSMauro Carvalho Chehab 			if (intf == NULL) {
954*0c0d06caSMauro Carvalho Chehab 				uvc_trace(UVC_TRACE_DESCR, "device %d "
955*0c0d06caSMauro Carvalho Chehab 					"interface %d doesn't exists\n",
956*0c0d06caSMauro Carvalho Chehab 					udev->devnum, i);
957*0c0d06caSMauro Carvalho Chehab 				continue;
958*0c0d06caSMauro Carvalho Chehab 			}
959*0c0d06caSMauro Carvalho Chehab 
960*0c0d06caSMauro Carvalho Chehab 			uvc_parse_streaming(dev, intf);
961*0c0d06caSMauro Carvalho Chehab 		}
962*0c0d06caSMauro Carvalho Chehab 		break;
963*0c0d06caSMauro Carvalho Chehab 
964*0c0d06caSMauro Carvalho Chehab 	case UVC_VC_INPUT_TERMINAL:
965*0c0d06caSMauro Carvalho Chehab 		if (buflen < 8) {
966*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
967*0c0d06caSMauro Carvalho Chehab 				"interface %d INPUT_TERMINAL error\n",
968*0c0d06caSMauro Carvalho Chehab 				udev->devnum, alts->desc.bInterfaceNumber);
969*0c0d06caSMauro Carvalho Chehab 			return -EINVAL;
970*0c0d06caSMauro Carvalho Chehab 		}
971*0c0d06caSMauro Carvalho Chehab 
972*0c0d06caSMauro Carvalho Chehab 		/* Make sure the terminal type MSB is not null, otherwise it
973*0c0d06caSMauro Carvalho Chehab 		 * could be confused with a unit.
974*0c0d06caSMauro Carvalho Chehab 		 */
975*0c0d06caSMauro Carvalho Chehab 		type = get_unaligned_le16(&buffer[4]);
976*0c0d06caSMauro Carvalho Chehab 		if ((type & 0xff00) == 0) {
977*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
978*0c0d06caSMauro Carvalho Chehab 				"interface %d INPUT_TERMINAL %d has invalid "
979*0c0d06caSMauro Carvalho Chehab 				"type 0x%04x, skipping\n", udev->devnum,
980*0c0d06caSMauro Carvalho Chehab 				alts->desc.bInterfaceNumber,
981*0c0d06caSMauro Carvalho Chehab 				buffer[3], type);
982*0c0d06caSMauro Carvalho Chehab 			return 0;
983*0c0d06caSMauro Carvalho Chehab 		}
984*0c0d06caSMauro Carvalho Chehab 
985*0c0d06caSMauro Carvalho Chehab 		n = 0;
986*0c0d06caSMauro Carvalho Chehab 		p = 0;
987*0c0d06caSMauro Carvalho Chehab 		len = 8;
988*0c0d06caSMauro Carvalho Chehab 
989*0c0d06caSMauro Carvalho Chehab 		if (type == UVC_ITT_CAMERA) {
990*0c0d06caSMauro Carvalho Chehab 			n = buflen >= 15 ? buffer[14] : 0;
991*0c0d06caSMauro Carvalho Chehab 			len = 15;
992*0c0d06caSMauro Carvalho Chehab 
993*0c0d06caSMauro Carvalho Chehab 		} else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) {
994*0c0d06caSMauro Carvalho Chehab 			n = buflen >= 9 ? buffer[8] : 0;
995*0c0d06caSMauro Carvalho Chehab 			p = buflen >= 10 + n ? buffer[9+n] : 0;
996*0c0d06caSMauro Carvalho Chehab 			len = 10;
997*0c0d06caSMauro Carvalho Chehab 		}
998*0c0d06caSMauro Carvalho Chehab 
999*0c0d06caSMauro Carvalho Chehab 		if (buflen < len + n + p) {
1000*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
1001*0c0d06caSMauro Carvalho Chehab 				"interface %d INPUT_TERMINAL error\n",
1002*0c0d06caSMauro Carvalho Chehab 				udev->devnum, alts->desc.bInterfaceNumber);
1003*0c0d06caSMauro Carvalho Chehab 			return -EINVAL;
1004*0c0d06caSMauro Carvalho Chehab 		}
1005*0c0d06caSMauro Carvalho Chehab 
1006*0c0d06caSMauro Carvalho Chehab 		term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3],
1007*0c0d06caSMauro Carvalho Chehab 					1, n + p);
1008*0c0d06caSMauro Carvalho Chehab 		if (term == NULL)
1009*0c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
1010*0c0d06caSMauro Carvalho Chehab 
1011*0c0d06caSMauro Carvalho Chehab 		if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) {
1012*0c0d06caSMauro Carvalho Chehab 			term->camera.bControlSize = n;
1013*0c0d06caSMauro Carvalho Chehab 			term->camera.bmControls = (__u8 *)term + sizeof *term;
1014*0c0d06caSMauro Carvalho Chehab 			term->camera.wObjectiveFocalLengthMin =
1015*0c0d06caSMauro Carvalho Chehab 				get_unaligned_le16(&buffer[8]);
1016*0c0d06caSMauro Carvalho Chehab 			term->camera.wObjectiveFocalLengthMax =
1017*0c0d06caSMauro Carvalho Chehab 				get_unaligned_le16(&buffer[10]);
1018*0c0d06caSMauro Carvalho Chehab 			term->camera.wOcularFocalLength =
1019*0c0d06caSMauro Carvalho Chehab 				get_unaligned_le16(&buffer[12]);
1020*0c0d06caSMauro Carvalho Chehab 			memcpy(term->camera.bmControls, &buffer[15], n);
1021*0c0d06caSMauro Carvalho Chehab 		} else if (UVC_ENTITY_TYPE(term) ==
1022*0c0d06caSMauro Carvalho Chehab 			   UVC_ITT_MEDIA_TRANSPORT_INPUT) {
1023*0c0d06caSMauro Carvalho Chehab 			term->media.bControlSize = n;
1024*0c0d06caSMauro Carvalho Chehab 			term->media.bmControls = (__u8 *)term + sizeof *term;
1025*0c0d06caSMauro Carvalho Chehab 			term->media.bTransportModeSize = p;
1026*0c0d06caSMauro Carvalho Chehab 			term->media.bmTransportModes = (__u8 *)term
1027*0c0d06caSMauro Carvalho Chehab 						     + sizeof *term + n;
1028*0c0d06caSMauro Carvalho Chehab 			memcpy(term->media.bmControls, &buffer[9], n);
1029*0c0d06caSMauro Carvalho Chehab 			memcpy(term->media.bmTransportModes, &buffer[10+n], p);
1030*0c0d06caSMauro Carvalho Chehab 		}
1031*0c0d06caSMauro Carvalho Chehab 
1032*0c0d06caSMauro Carvalho Chehab 		if (buffer[7] != 0)
1033*0c0d06caSMauro Carvalho Chehab 			usb_string(udev, buffer[7], term->name,
1034*0c0d06caSMauro Carvalho Chehab 				   sizeof term->name);
1035*0c0d06caSMauro Carvalho Chehab 		else if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA)
1036*0c0d06caSMauro Carvalho Chehab 			sprintf(term->name, "Camera %u", buffer[3]);
1037*0c0d06caSMauro Carvalho Chehab 		else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT)
1038*0c0d06caSMauro Carvalho Chehab 			sprintf(term->name, "Media %u", buffer[3]);
1039*0c0d06caSMauro Carvalho Chehab 		else
1040*0c0d06caSMauro Carvalho Chehab 			sprintf(term->name, "Input %u", buffer[3]);
1041*0c0d06caSMauro Carvalho Chehab 
1042*0c0d06caSMauro Carvalho Chehab 		list_add_tail(&term->list, &dev->entities);
1043*0c0d06caSMauro Carvalho Chehab 		break;
1044*0c0d06caSMauro Carvalho Chehab 
1045*0c0d06caSMauro Carvalho Chehab 	case UVC_VC_OUTPUT_TERMINAL:
1046*0c0d06caSMauro Carvalho Chehab 		if (buflen < 9) {
1047*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
1048*0c0d06caSMauro Carvalho Chehab 				"interface %d OUTPUT_TERMINAL error\n",
1049*0c0d06caSMauro Carvalho Chehab 				udev->devnum, alts->desc.bInterfaceNumber);
1050*0c0d06caSMauro Carvalho Chehab 			return -EINVAL;
1051*0c0d06caSMauro Carvalho Chehab 		}
1052*0c0d06caSMauro Carvalho Chehab 
1053*0c0d06caSMauro Carvalho Chehab 		/* Make sure the terminal type MSB is not null, otherwise it
1054*0c0d06caSMauro Carvalho Chehab 		 * could be confused with a unit.
1055*0c0d06caSMauro Carvalho Chehab 		 */
1056*0c0d06caSMauro Carvalho Chehab 		type = get_unaligned_le16(&buffer[4]);
1057*0c0d06caSMauro Carvalho Chehab 		if ((type & 0xff00) == 0) {
1058*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
1059*0c0d06caSMauro Carvalho Chehab 				"interface %d OUTPUT_TERMINAL %d has invalid "
1060*0c0d06caSMauro Carvalho Chehab 				"type 0x%04x, skipping\n", udev->devnum,
1061*0c0d06caSMauro Carvalho Chehab 				alts->desc.bInterfaceNumber, buffer[3], type);
1062*0c0d06caSMauro Carvalho Chehab 			return 0;
1063*0c0d06caSMauro Carvalho Chehab 		}
1064*0c0d06caSMauro Carvalho Chehab 
1065*0c0d06caSMauro Carvalho Chehab 		term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3],
1066*0c0d06caSMauro Carvalho Chehab 					1, 0);
1067*0c0d06caSMauro Carvalho Chehab 		if (term == NULL)
1068*0c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
1069*0c0d06caSMauro Carvalho Chehab 
1070*0c0d06caSMauro Carvalho Chehab 		memcpy(term->baSourceID, &buffer[7], 1);
1071*0c0d06caSMauro Carvalho Chehab 
1072*0c0d06caSMauro Carvalho Chehab 		if (buffer[8] != 0)
1073*0c0d06caSMauro Carvalho Chehab 			usb_string(udev, buffer[8], term->name,
1074*0c0d06caSMauro Carvalho Chehab 				   sizeof term->name);
1075*0c0d06caSMauro Carvalho Chehab 		else
1076*0c0d06caSMauro Carvalho Chehab 			sprintf(term->name, "Output %u", buffer[3]);
1077*0c0d06caSMauro Carvalho Chehab 
1078*0c0d06caSMauro Carvalho Chehab 		list_add_tail(&term->list, &dev->entities);
1079*0c0d06caSMauro Carvalho Chehab 		break;
1080*0c0d06caSMauro Carvalho Chehab 
1081*0c0d06caSMauro Carvalho Chehab 	case UVC_VC_SELECTOR_UNIT:
1082*0c0d06caSMauro Carvalho Chehab 		p = buflen >= 5 ? buffer[4] : 0;
1083*0c0d06caSMauro Carvalho Chehab 
1084*0c0d06caSMauro Carvalho Chehab 		if (buflen < 5 || buflen < 6 + p) {
1085*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
1086*0c0d06caSMauro Carvalho Chehab 				"interface %d SELECTOR_UNIT error\n",
1087*0c0d06caSMauro Carvalho Chehab 				udev->devnum, alts->desc.bInterfaceNumber);
1088*0c0d06caSMauro Carvalho Chehab 			return -EINVAL;
1089*0c0d06caSMauro Carvalho Chehab 		}
1090*0c0d06caSMauro Carvalho Chehab 
1091*0c0d06caSMauro Carvalho Chehab 		unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0);
1092*0c0d06caSMauro Carvalho Chehab 		if (unit == NULL)
1093*0c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
1094*0c0d06caSMauro Carvalho Chehab 
1095*0c0d06caSMauro Carvalho Chehab 		memcpy(unit->baSourceID, &buffer[5], p);
1096*0c0d06caSMauro Carvalho Chehab 
1097*0c0d06caSMauro Carvalho Chehab 		if (buffer[5+p] != 0)
1098*0c0d06caSMauro Carvalho Chehab 			usb_string(udev, buffer[5+p], unit->name,
1099*0c0d06caSMauro Carvalho Chehab 				   sizeof unit->name);
1100*0c0d06caSMauro Carvalho Chehab 		else
1101*0c0d06caSMauro Carvalho Chehab 			sprintf(unit->name, "Selector %u", buffer[3]);
1102*0c0d06caSMauro Carvalho Chehab 
1103*0c0d06caSMauro Carvalho Chehab 		list_add_tail(&unit->list, &dev->entities);
1104*0c0d06caSMauro Carvalho Chehab 		break;
1105*0c0d06caSMauro Carvalho Chehab 
1106*0c0d06caSMauro Carvalho Chehab 	case UVC_VC_PROCESSING_UNIT:
1107*0c0d06caSMauro Carvalho Chehab 		n = buflen >= 8 ? buffer[7] : 0;
1108*0c0d06caSMauro Carvalho Chehab 		p = dev->uvc_version >= 0x0110 ? 10 : 9;
1109*0c0d06caSMauro Carvalho Chehab 
1110*0c0d06caSMauro Carvalho Chehab 		if (buflen < p + n) {
1111*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
1112*0c0d06caSMauro Carvalho Chehab 				"interface %d PROCESSING_UNIT error\n",
1113*0c0d06caSMauro Carvalho Chehab 				udev->devnum, alts->desc.bInterfaceNumber);
1114*0c0d06caSMauro Carvalho Chehab 			return -EINVAL;
1115*0c0d06caSMauro Carvalho Chehab 		}
1116*0c0d06caSMauro Carvalho Chehab 
1117*0c0d06caSMauro Carvalho Chehab 		unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n);
1118*0c0d06caSMauro Carvalho Chehab 		if (unit == NULL)
1119*0c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
1120*0c0d06caSMauro Carvalho Chehab 
1121*0c0d06caSMauro Carvalho Chehab 		memcpy(unit->baSourceID, &buffer[4], 1);
1122*0c0d06caSMauro Carvalho Chehab 		unit->processing.wMaxMultiplier =
1123*0c0d06caSMauro Carvalho Chehab 			get_unaligned_le16(&buffer[5]);
1124*0c0d06caSMauro Carvalho Chehab 		unit->processing.bControlSize = buffer[7];
1125*0c0d06caSMauro Carvalho Chehab 		unit->processing.bmControls = (__u8 *)unit + sizeof *unit;
1126*0c0d06caSMauro Carvalho Chehab 		memcpy(unit->processing.bmControls, &buffer[8], n);
1127*0c0d06caSMauro Carvalho Chehab 		if (dev->uvc_version >= 0x0110)
1128*0c0d06caSMauro Carvalho Chehab 			unit->processing.bmVideoStandards = buffer[9+n];
1129*0c0d06caSMauro Carvalho Chehab 
1130*0c0d06caSMauro Carvalho Chehab 		if (buffer[8+n] != 0)
1131*0c0d06caSMauro Carvalho Chehab 			usb_string(udev, buffer[8+n], unit->name,
1132*0c0d06caSMauro Carvalho Chehab 				   sizeof unit->name);
1133*0c0d06caSMauro Carvalho Chehab 		else
1134*0c0d06caSMauro Carvalho Chehab 			sprintf(unit->name, "Processing %u", buffer[3]);
1135*0c0d06caSMauro Carvalho Chehab 
1136*0c0d06caSMauro Carvalho Chehab 		list_add_tail(&unit->list, &dev->entities);
1137*0c0d06caSMauro Carvalho Chehab 		break;
1138*0c0d06caSMauro Carvalho Chehab 
1139*0c0d06caSMauro Carvalho Chehab 	case UVC_VC_EXTENSION_UNIT:
1140*0c0d06caSMauro Carvalho Chehab 		p = buflen >= 22 ? buffer[21] : 0;
1141*0c0d06caSMauro Carvalho Chehab 		n = buflen >= 24 + p ? buffer[22+p] : 0;
1142*0c0d06caSMauro Carvalho Chehab 
1143*0c0d06caSMauro Carvalho Chehab 		if (buflen < 24 + p + n) {
1144*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
1145*0c0d06caSMauro Carvalho Chehab 				"interface %d EXTENSION_UNIT error\n",
1146*0c0d06caSMauro Carvalho Chehab 				udev->devnum, alts->desc.bInterfaceNumber);
1147*0c0d06caSMauro Carvalho Chehab 			return -EINVAL;
1148*0c0d06caSMauro Carvalho Chehab 		}
1149*0c0d06caSMauro Carvalho Chehab 
1150*0c0d06caSMauro Carvalho Chehab 		unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n);
1151*0c0d06caSMauro Carvalho Chehab 		if (unit == NULL)
1152*0c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
1153*0c0d06caSMauro Carvalho Chehab 
1154*0c0d06caSMauro Carvalho Chehab 		memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
1155*0c0d06caSMauro Carvalho Chehab 		unit->extension.bNumControls = buffer[20];
1156*0c0d06caSMauro Carvalho Chehab 		memcpy(unit->baSourceID, &buffer[22], p);
1157*0c0d06caSMauro Carvalho Chehab 		unit->extension.bControlSize = buffer[22+p];
1158*0c0d06caSMauro Carvalho Chehab 		unit->extension.bmControls = (__u8 *)unit + sizeof *unit;
1159*0c0d06caSMauro Carvalho Chehab 		memcpy(unit->extension.bmControls, &buffer[23+p], n);
1160*0c0d06caSMauro Carvalho Chehab 
1161*0c0d06caSMauro Carvalho Chehab 		if (buffer[23+p+n] != 0)
1162*0c0d06caSMauro Carvalho Chehab 			usb_string(udev, buffer[23+p+n], unit->name,
1163*0c0d06caSMauro Carvalho Chehab 				   sizeof unit->name);
1164*0c0d06caSMauro Carvalho Chehab 		else
1165*0c0d06caSMauro Carvalho Chehab 			sprintf(unit->name, "Extension %u", buffer[3]);
1166*0c0d06caSMauro Carvalho Chehab 
1167*0c0d06caSMauro Carvalho Chehab 		list_add_tail(&unit->list, &dev->entities);
1168*0c0d06caSMauro Carvalho Chehab 		break;
1169*0c0d06caSMauro Carvalho Chehab 
1170*0c0d06caSMauro Carvalho Chehab 	default:
1171*0c0d06caSMauro Carvalho Chehab 		uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE "
1172*0c0d06caSMauro Carvalho Chehab 			"descriptor (%u)\n", buffer[2]);
1173*0c0d06caSMauro Carvalho Chehab 		break;
1174*0c0d06caSMauro Carvalho Chehab 	}
1175*0c0d06caSMauro Carvalho Chehab 
1176*0c0d06caSMauro Carvalho Chehab 	return 0;
1177*0c0d06caSMauro Carvalho Chehab }
1178*0c0d06caSMauro Carvalho Chehab 
1179*0c0d06caSMauro Carvalho Chehab static int uvc_parse_control(struct uvc_device *dev)
1180*0c0d06caSMauro Carvalho Chehab {
1181*0c0d06caSMauro Carvalho Chehab 	struct usb_host_interface *alts = dev->intf->cur_altsetting;
1182*0c0d06caSMauro Carvalho Chehab 	unsigned char *buffer = alts->extra;
1183*0c0d06caSMauro Carvalho Chehab 	int buflen = alts->extralen;
1184*0c0d06caSMauro Carvalho Chehab 	int ret;
1185*0c0d06caSMauro Carvalho Chehab 
1186*0c0d06caSMauro Carvalho Chehab 	/* Parse the default alternate setting only, as the UVC specification
1187*0c0d06caSMauro Carvalho Chehab 	 * defines a single alternate setting, the default alternate setting
1188*0c0d06caSMauro Carvalho Chehab 	 * zero.
1189*0c0d06caSMauro Carvalho Chehab 	 */
1190*0c0d06caSMauro Carvalho Chehab 
1191*0c0d06caSMauro Carvalho Chehab 	while (buflen > 2) {
1192*0c0d06caSMauro Carvalho Chehab 		if (uvc_parse_vendor_control(dev, buffer, buflen) ||
1193*0c0d06caSMauro Carvalho Chehab 		    buffer[1] != USB_DT_CS_INTERFACE)
1194*0c0d06caSMauro Carvalho Chehab 			goto next_descriptor;
1195*0c0d06caSMauro Carvalho Chehab 
1196*0c0d06caSMauro Carvalho Chehab 		if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0)
1197*0c0d06caSMauro Carvalho Chehab 			return ret;
1198*0c0d06caSMauro Carvalho Chehab 
1199*0c0d06caSMauro Carvalho Chehab next_descriptor:
1200*0c0d06caSMauro Carvalho Chehab 		buflen -= buffer[0];
1201*0c0d06caSMauro Carvalho Chehab 		buffer += buffer[0];
1202*0c0d06caSMauro Carvalho Chehab 	}
1203*0c0d06caSMauro Carvalho Chehab 
1204*0c0d06caSMauro Carvalho Chehab 	/* Check if the optional status endpoint is present. Built-in iSight
1205*0c0d06caSMauro Carvalho Chehab 	 * webcams have an interrupt endpoint but spit proprietary data that
1206*0c0d06caSMauro Carvalho Chehab 	 * don't conform to the UVC status endpoint messages. Don't try to
1207*0c0d06caSMauro Carvalho Chehab 	 * handle the interrupt endpoint for those cameras.
1208*0c0d06caSMauro Carvalho Chehab 	 */
1209*0c0d06caSMauro Carvalho Chehab 	if (alts->desc.bNumEndpoints == 1 &&
1210*0c0d06caSMauro Carvalho Chehab 	    !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) {
1211*0c0d06caSMauro Carvalho Chehab 		struct usb_host_endpoint *ep = &alts->endpoint[0];
1212*0c0d06caSMauro Carvalho Chehab 		struct usb_endpoint_descriptor *desc = &ep->desc;
1213*0c0d06caSMauro Carvalho Chehab 
1214*0c0d06caSMauro Carvalho Chehab 		if (usb_endpoint_is_int_in(desc) &&
1215*0c0d06caSMauro Carvalho Chehab 		    le16_to_cpu(desc->wMaxPacketSize) >= 8 &&
1216*0c0d06caSMauro Carvalho Chehab 		    desc->bInterval != 0) {
1217*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint "
1218*0c0d06caSMauro Carvalho Chehab 				"(addr %02x).\n", desc->bEndpointAddress);
1219*0c0d06caSMauro Carvalho Chehab 			dev->int_ep = ep;
1220*0c0d06caSMauro Carvalho Chehab 		}
1221*0c0d06caSMauro Carvalho Chehab 	}
1222*0c0d06caSMauro Carvalho Chehab 
1223*0c0d06caSMauro Carvalho Chehab 	return 0;
1224*0c0d06caSMauro Carvalho Chehab }
1225*0c0d06caSMauro Carvalho Chehab 
1226*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
1227*0c0d06caSMauro Carvalho Chehab  * UVC device scan
1228*0c0d06caSMauro Carvalho Chehab  */
1229*0c0d06caSMauro Carvalho Chehab 
1230*0c0d06caSMauro Carvalho Chehab /*
1231*0c0d06caSMauro Carvalho Chehab  * Scan the UVC descriptors to locate a chain starting at an Output Terminal
1232*0c0d06caSMauro Carvalho Chehab  * and containing the following units:
1233*0c0d06caSMauro Carvalho Chehab  *
1234*0c0d06caSMauro Carvalho Chehab  * - one or more Output Terminals (USB Streaming or Display)
1235*0c0d06caSMauro Carvalho Chehab  * - zero or one Processing Unit
1236*0c0d06caSMauro Carvalho Chehab  * - zero, one or more single-input Selector Units
1237*0c0d06caSMauro Carvalho Chehab  * - zero or one multiple-input Selector Units, provided all inputs are
1238*0c0d06caSMauro Carvalho Chehab  *   connected to input terminals
1239*0c0d06caSMauro Carvalho Chehab  * - zero, one or mode single-input Extension Units
1240*0c0d06caSMauro Carvalho Chehab  * - one or more Input Terminals (Camera, External or USB Streaming)
1241*0c0d06caSMauro Carvalho Chehab  *
1242*0c0d06caSMauro Carvalho Chehab  * The terminal and units must match on of the following structures:
1243*0c0d06caSMauro Carvalho Chehab  *
1244*0c0d06caSMauro Carvalho Chehab  * ITT_*(0) -> +---------+    +---------+    +---------+ -> TT_STREAMING(0)
1245*0c0d06caSMauro Carvalho Chehab  * ...         | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} |    ...
1246*0c0d06caSMauro Carvalho Chehab  * ITT_*(n) -> +---------+    +---------+    +---------+ -> TT_STREAMING(n)
1247*0c0d06caSMauro Carvalho Chehab  *
1248*0c0d06caSMauro Carvalho Chehab  *                 +---------+    +---------+ -> OTT_*(0)
1249*0c0d06caSMauro Carvalho Chehab  * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} |    ...
1250*0c0d06caSMauro Carvalho Chehab  *                 +---------+    +---------+ -> OTT_*(n)
1251*0c0d06caSMauro Carvalho Chehab  *
1252*0c0d06caSMauro Carvalho Chehab  * The Processing Unit and Extension Units can be in any order. Additional
1253*0c0d06caSMauro Carvalho Chehab  * Extension Units connected to the main chain as single-unit branches are
1254*0c0d06caSMauro Carvalho Chehab  * also supported. Single-input Selector Units are ignored.
1255*0c0d06caSMauro Carvalho Chehab  */
1256*0c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
1257*0c0d06caSMauro Carvalho Chehab 	struct uvc_entity *entity)
1258*0c0d06caSMauro Carvalho Chehab {
1259*0c0d06caSMauro Carvalho Chehab 	switch (UVC_ENTITY_TYPE(entity)) {
1260*0c0d06caSMauro Carvalho Chehab 	case UVC_VC_EXTENSION_UNIT:
1261*0c0d06caSMauro Carvalho Chehab 		if (uvc_trace_param & UVC_TRACE_PROBE)
1262*0c0d06caSMauro Carvalho Chehab 			printk(" <- XU %d", entity->id);
1263*0c0d06caSMauro Carvalho Chehab 
1264*0c0d06caSMauro Carvalho Chehab 		if (entity->bNrInPins != 1) {
1265*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more "
1266*0c0d06caSMauro Carvalho Chehab 				"than 1 input pin.\n", entity->id);
1267*0c0d06caSMauro Carvalho Chehab 			return -1;
1268*0c0d06caSMauro Carvalho Chehab 		}
1269*0c0d06caSMauro Carvalho Chehab 
1270*0c0d06caSMauro Carvalho Chehab 		break;
1271*0c0d06caSMauro Carvalho Chehab 
1272*0c0d06caSMauro Carvalho Chehab 	case UVC_VC_PROCESSING_UNIT:
1273*0c0d06caSMauro Carvalho Chehab 		if (uvc_trace_param & UVC_TRACE_PROBE)
1274*0c0d06caSMauro Carvalho Chehab 			printk(" <- PU %d", entity->id);
1275*0c0d06caSMauro Carvalho Chehab 
1276*0c0d06caSMauro Carvalho Chehab 		if (chain->processing != NULL) {
1277*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "Found multiple "
1278*0c0d06caSMauro Carvalho Chehab 				"Processing Units in chain.\n");
1279*0c0d06caSMauro Carvalho Chehab 			return -1;
1280*0c0d06caSMauro Carvalho Chehab 		}
1281*0c0d06caSMauro Carvalho Chehab 
1282*0c0d06caSMauro Carvalho Chehab 		chain->processing = entity;
1283*0c0d06caSMauro Carvalho Chehab 		break;
1284*0c0d06caSMauro Carvalho Chehab 
1285*0c0d06caSMauro Carvalho Chehab 	case UVC_VC_SELECTOR_UNIT:
1286*0c0d06caSMauro Carvalho Chehab 		if (uvc_trace_param & UVC_TRACE_PROBE)
1287*0c0d06caSMauro Carvalho Chehab 			printk(" <- SU %d", entity->id);
1288*0c0d06caSMauro Carvalho Chehab 
1289*0c0d06caSMauro Carvalho Chehab 		/* Single-input selector units are ignored. */
1290*0c0d06caSMauro Carvalho Chehab 		if (entity->bNrInPins == 1)
1291*0c0d06caSMauro Carvalho Chehab 			break;
1292*0c0d06caSMauro Carvalho Chehab 
1293*0c0d06caSMauro Carvalho Chehab 		if (chain->selector != NULL) {
1294*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector "
1295*0c0d06caSMauro Carvalho Chehab 				"Units in chain.\n");
1296*0c0d06caSMauro Carvalho Chehab 			return -1;
1297*0c0d06caSMauro Carvalho Chehab 		}
1298*0c0d06caSMauro Carvalho Chehab 
1299*0c0d06caSMauro Carvalho Chehab 		chain->selector = entity;
1300*0c0d06caSMauro Carvalho Chehab 		break;
1301*0c0d06caSMauro Carvalho Chehab 
1302*0c0d06caSMauro Carvalho Chehab 	case UVC_ITT_VENDOR_SPECIFIC:
1303*0c0d06caSMauro Carvalho Chehab 	case UVC_ITT_CAMERA:
1304*0c0d06caSMauro Carvalho Chehab 	case UVC_ITT_MEDIA_TRANSPORT_INPUT:
1305*0c0d06caSMauro Carvalho Chehab 		if (uvc_trace_param & UVC_TRACE_PROBE)
1306*0c0d06caSMauro Carvalho Chehab 			printk(" <- IT %d\n", entity->id);
1307*0c0d06caSMauro Carvalho Chehab 
1308*0c0d06caSMauro Carvalho Chehab 		break;
1309*0c0d06caSMauro Carvalho Chehab 
1310*0c0d06caSMauro Carvalho Chehab 	case UVC_OTT_VENDOR_SPECIFIC:
1311*0c0d06caSMauro Carvalho Chehab 	case UVC_OTT_DISPLAY:
1312*0c0d06caSMauro Carvalho Chehab 	case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
1313*0c0d06caSMauro Carvalho Chehab 		if (uvc_trace_param & UVC_TRACE_PROBE)
1314*0c0d06caSMauro Carvalho Chehab 			printk(" OT %d", entity->id);
1315*0c0d06caSMauro Carvalho Chehab 
1316*0c0d06caSMauro Carvalho Chehab 		break;
1317*0c0d06caSMauro Carvalho Chehab 
1318*0c0d06caSMauro Carvalho Chehab 	case UVC_TT_STREAMING:
1319*0c0d06caSMauro Carvalho Chehab 		if (UVC_ENTITY_IS_ITERM(entity)) {
1320*0c0d06caSMauro Carvalho Chehab 			if (uvc_trace_param & UVC_TRACE_PROBE)
1321*0c0d06caSMauro Carvalho Chehab 				printk(" <- IT %d\n", entity->id);
1322*0c0d06caSMauro Carvalho Chehab 		} else {
1323*0c0d06caSMauro Carvalho Chehab 			if (uvc_trace_param & UVC_TRACE_PROBE)
1324*0c0d06caSMauro Carvalho Chehab 				printk(" OT %d", entity->id);
1325*0c0d06caSMauro Carvalho Chehab 		}
1326*0c0d06caSMauro Carvalho Chehab 
1327*0c0d06caSMauro Carvalho Chehab 		break;
1328*0c0d06caSMauro Carvalho Chehab 
1329*0c0d06caSMauro Carvalho Chehab 	default:
1330*0c0d06caSMauro Carvalho Chehab 		uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type "
1331*0c0d06caSMauro Carvalho Chehab 			"0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
1332*0c0d06caSMauro Carvalho Chehab 		return -1;
1333*0c0d06caSMauro Carvalho Chehab 	}
1334*0c0d06caSMauro Carvalho Chehab 
1335*0c0d06caSMauro Carvalho Chehab 	list_add_tail(&entity->chain, &chain->entities);
1336*0c0d06caSMauro Carvalho Chehab 	return 0;
1337*0c0d06caSMauro Carvalho Chehab }
1338*0c0d06caSMauro Carvalho Chehab 
1339*0c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
1340*0c0d06caSMauro Carvalho Chehab 	struct uvc_entity *entity, struct uvc_entity *prev)
1341*0c0d06caSMauro Carvalho Chehab {
1342*0c0d06caSMauro Carvalho Chehab 	struct uvc_entity *forward;
1343*0c0d06caSMauro Carvalho Chehab 	int found;
1344*0c0d06caSMauro Carvalho Chehab 
1345*0c0d06caSMauro Carvalho Chehab 	/* Forward scan */
1346*0c0d06caSMauro Carvalho Chehab 	forward = NULL;
1347*0c0d06caSMauro Carvalho Chehab 	found = 0;
1348*0c0d06caSMauro Carvalho Chehab 
1349*0c0d06caSMauro Carvalho Chehab 	while (1) {
1350*0c0d06caSMauro Carvalho Chehab 		forward = uvc_entity_by_reference(chain->dev, entity->id,
1351*0c0d06caSMauro Carvalho Chehab 			forward);
1352*0c0d06caSMauro Carvalho Chehab 		if (forward == NULL)
1353*0c0d06caSMauro Carvalho Chehab 			break;
1354*0c0d06caSMauro Carvalho Chehab 		if (forward == prev)
1355*0c0d06caSMauro Carvalho Chehab 			continue;
1356*0c0d06caSMauro Carvalho Chehab 
1357*0c0d06caSMauro Carvalho Chehab 		switch (UVC_ENTITY_TYPE(forward)) {
1358*0c0d06caSMauro Carvalho Chehab 		case UVC_VC_EXTENSION_UNIT:
1359*0c0d06caSMauro Carvalho Chehab 			if (forward->bNrInPins != 1) {
1360*0c0d06caSMauro Carvalho Chehab 				uvc_trace(UVC_TRACE_DESCR, "Extension unit %d "
1361*0c0d06caSMauro Carvalho Chehab 					  "has more than 1 input pin.\n",
1362*0c0d06caSMauro Carvalho Chehab 					  entity->id);
1363*0c0d06caSMauro Carvalho Chehab 				return -EINVAL;
1364*0c0d06caSMauro Carvalho Chehab 			}
1365*0c0d06caSMauro Carvalho Chehab 
1366*0c0d06caSMauro Carvalho Chehab 			list_add_tail(&forward->chain, &chain->entities);
1367*0c0d06caSMauro Carvalho Chehab 			if (uvc_trace_param & UVC_TRACE_PROBE) {
1368*0c0d06caSMauro Carvalho Chehab 				if (!found)
1369*0c0d06caSMauro Carvalho Chehab 					printk(" (->");
1370*0c0d06caSMauro Carvalho Chehab 
1371*0c0d06caSMauro Carvalho Chehab 				printk(" XU %d", forward->id);
1372*0c0d06caSMauro Carvalho Chehab 				found = 1;
1373*0c0d06caSMauro Carvalho Chehab 			}
1374*0c0d06caSMauro Carvalho Chehab 			break;
1375*0c0d06caSMauro Carvalho Chehab 
1376*0c0d06caSMauro Carvalho Chehab 		case UVC_OTT_VENDOR_SPECIFIC:
1377*0c0d06caSMauro Carvalho Chehab 		case UVC_OTT_DISPLAY:
1378*0c0d06caSMauro Carvalho Chehab 		case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
1379*0c0d06caSMauro Carvalho Chehab 		case UVC_TT_STREAMING:
1380*0c0d06caSMauro Carvalho Chehab 			if (UVC_ENTITY_IS_ITERM(forward)) {
1381*0c0d06caSMauro Carvalho Chehab 				uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
1382*0c0d06caSMauro Carvalho Chehab 					"terminal %u.\n", forward->id);
1383*0c0d06caSMauro Carvalho Chehab 				return -EINVAL;
1384*0c0d06caSMauro Carvalho Chehab 			}
1385*0c0d06caSMauro Carvalho Chehab 
1386*0c0d06caSMauro Carvalho Chehab 			list_add_tail(&forward->chain, &chain->entities);
1387*0c0d06caSMauro Carvalho Chehab 			if (uvc_trace_param & UVC_TRACE_PROBE) {
1388*0c0d06caSMauro Carvalho Chehab 				if (!found)
1389*0c0d06caSMauro Carvalho Chehab 					printk(" (->");
1390*0c0d06caSMauro Carvalho Chehab 
1391*0c0d06caSMauro Carvalho Chehab 				printk(" OT %d", forward->id);
1392*0c0d06caSMauro Carvalho Chehab 				found = 1;
1393*0c0d06caSMauro Carvalho Chehab 			}
1394*0c0d06caSMauro Carvalho Chehab 			break;
1395*0c0d06caSMauro Carvalho Chehab 		}
1396*0c0d06caSMauro Carvalho Chehab 	}
1397*0c0d06caSMauro Carvalho Chehab 	if (found)
1398*0c0d06caSMauro Carvalho Chehab 		printk(")");
1399*0c0d06caSMauro Carvalho Chehab 
1400*0c0d06caSMauro Carvalho Chehab 	return 0;
1401*0c0d06caSMauro Carvalho Chehab }
1402*0c0d06caSMauro Carvalho Chehab 
1403*0c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
1404*0c0d06caSMauro Carvalho Chehab 	struct uvc_entity **_entity)
1405*0c0d06caSMauro Carvalho Chehab {
1406*0c0d06caSMauro Carvalho Chehab 	struct uvc_entity *entity = *_entity;
1407*0c0d06caSMauro Carvalho Chehab 	struct uvc_entity *term;
1408*0c0d06caSMauro Carvalho Chehab 	int id = -EINVAL, i;
1409*0c0d06caSMauro Carvalho Chehab 
1410*0c0d06caSMauro Carvalho Chehab 	switch (UVC_ENTITY_TYPE(entity)) {
1411*0c0d06caSMauro Carvalho Chehab 	case UVC_VC_EXTENSION_UNIT:
1412*0c0d06caSMauro Carvalho Chehab 	case UVC_VC_PROCESSING_UNIT:
1413*0c0d06caSMauro Carvalho Chehab 		id = entity->baSourceID[0];
1414*0c0d06caSMauro Carvalho Chehab 		break;
1415*0c0d06caSMauro Carvalho Chehab 
1416*0c0d06caSMauro Carvalho Chehab 	case UVC_VC_SELECTOR_UNIT:
1417*0c0d06caSMauro Carvalho Chehab 		/* Single-input selector units are ignored. */
1418*0c0d06caSMauro Carvalho Chehab 		if (entity->bNrInPins == 1) {
1419*0c0d06caSMauro Carvalho Chehab 			id = entity->baSourceID[0];
1420*0c0d06caSMauro Carvalho Chehab 			break;
1421*0c0d06caSMauro Carvalho Chehab 		}
1422*0c0d06caSMauro Carvalho Chehab 
1423*0c0d06caSMauro Carvalho Chehab 		if (uvc_trace_param & UVC_TRACE_PROBE)
1424*0c0d06caSMauro Carvalho Chehab 			printk(" <- IT");
1425*0c0d06caSMauro Carvalho Chehab 
1426*0c0d06caSMauro Carvalho Chehab 		chain->selector = entity;
1427*0c0d06caSMauro Carvalho Chehab 		for (i = 0; i < entity->bNrInPins; ++i) {
1428*0c0d06caSMauro Carvalho Chehab 			id = entity->baSourceID[i];
1429*0c0d06caSMauro Carvalho Chehab 			term = uvc_entity_by_id(chain->dev, id);
1430*0c0d06caSMauro Carvalho Chehab 			if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
1431*0c0d06caSMauro Carvalho Chehab 				uvc_trace(UVC_TRACE_DESCR, "Selector unit %d "
1432*0c0d06caSMauro Carvalho Chehab 					"input %d isn't connected to an "
1433*0c0d06caSMauro Carvalho Chehab 					"input terminal\n", entity->id, i);
1434*0c0d06caSMauro Carvalho Chehab 				return -1;
1435*0c0d06caSMauro Carvalho Chehab 			}
1436*0c0d06caSMauro Carvalho Chehab 
1437*0c0d06caSMauro Carvalho Chehab 			if (uvc_trace_param & UVC_TRACE_PROBE)
1438*0c0d06caSMauro Carvalho Chehab 				printk(" %d", term->id);
1439*0c0d06caSMauro Carvalho Chehab 
1440*0c0d06caSMauro Carvalho Chehab 			list_add_tail(&term->chain, &chain->entities);
1441*0c0d06caSMauro Carvalho Chehab 			uvc_scan_chain_forward(chain, term, entity);
1442*0c0d06caSMauro Carvalho Chehab 		}
1443*0c0d06caSMauro Carvalho Chehab 
1444*0c0d06caSMauro Carvalho Chehab 		if (uvc_trace_param & UVC_TRACE_PROBE)
1445*0c0d06caSMauro Carvalho Chehab 			printk("\n");
1446*0c0d06caSMauro Carvalho Chehab 
1447*0c0d06caSMauro Carvalho Chehab 		id = 0;
1448*0c0d06caSMauro Carvalho Chehab 		break;
1449*0c0d06caSMauro Carvalho Chehab 
1450*0c0d06caSMauro Carvalho Chehab 	case UVC_ITT_VENDOR_SPECIFIC:
1451*0c0d06caSMauro Carvalho Chehab 	case UVC_ITT_CAMERA:
1452*0c0d06caSMauro Carvalho Chehab 	case UVC_ITT_MEDIA_TRANSPORT_INPUT:
1453*0c0d06caSMauro Carvalho Chehab 	case UVC_OTT_VENDOR_SPECIFIC:
1454*0c0d06caSMauro Carvalho Chehab 	case UVC_OTT_DISPLAY:
1455*0c0d06caSMauro Carvalho Chehab 	case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
1456*0c0d06caSMauro Carvalho Chehab 	case UVC_TT_STREAMING:
1457*0c0d06caSMauro Carvalho Chehab 		id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0;
1458*0c0d06caSMauro Carvalho Chehab 		break;
1459*0c0d06caSMauro Carvalho Chehab 	}
1460*0c0d06caSMauro Carvalho Chehab 
1461*0c0d06caSMauro Carvalho Chehab 	if (id <= 0) {
1462*0c0d06caSMauro Carvalho Chehab 		*_entity = NULL;
1463*0c0d06caSMauro Carvalho Chehab 		return id;
1464*0c0d06caSMauro Carvalho Chehab 	}
1465*0c0d06caSMauro Carvalho Chehab 
1466*0c0d06caSMauro Carvalho Chehab 	entity = uvc_entity_by_id(chain->dev, id);
1467*0c0d06caSMauro Carvalho Chehab 	if (entity == NULL) {
1468*0c0d06caSMauro Carvalho Chehab 		uvc_trace(UVC_TRACE_DESCR, "Found reference to "
1469*0c0d06caSMauro Carvalho Chehab 			"unknown entity %d.\n", id);
1470*0c0d06caSMauro Carvalho Chehab 		return -EINVAL;
1471*0c0d06caSMauro Carvalho Chehab 	}
1472*0c0d06caSMauro Carvalho Chehab 
1473*0c0d06caSMauro Carvalho Chehab 	*_entity = entity;
1474*0c0d06caSMauro Carvalho Chehab 	return 0;
1475*0c0d06caSMauro Carvalho Chehab }
1476*0c0d06caSMauro Carvalho Chehab 
1477*0c0d06caSMauro Carvalho Chehab static int uvc_scan_chain(struct uvc_video_chain *chain,
1478*0c0d06caSMauro Carvalho Chehab 			  struct uvc_entity *term)
1479*0c0d06caSMauro Carvalho Chehab {
1480*0c0d06caSMauro Carvalho Chehab 	struct uvc_entity *entity, *prev;
1481*0c0d06caSMauro Carvalho Chehab 
1482*0c0d06caSMauro Carvalho Chehab 	uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:");
1483*0c0d06caSMauro Carvalho Chehab 
1484*0c0d06caSMauro Carvalho Chehab 	entity = term;
1485*0c0d06caSMauro Carvalho Chehab 	prev = NULL;
1486*0c0d06caSMauro Carvalho Chehab 
1487*0c0d06caSMauro Carvalho Chehab 	while (entity != NULL) {
1488*0c0d06caSMauro Carvalho Chehab 		/* Entity must not be part of an existing chain */
1489*0c0d06caSMauro Carvalho Chehab 		if (entity->chain.next || entity->chain.prev) {
1490*0c0d06caSMauro Carvalho Chehab 			uvc_trace(UVC_TRACE_DESCR, "Found reference to "
1491*0c0d06caSMauro Carvalho Chehab 				"entity %d already in chain.\n", entity->id);
1492*0c0d06caSMauro Carvalho Chehab 			return -EINVAL;
1493*0c0d06caSMauro Carvalho Chehab 		}
1494*0c0d06caSMauro Carvalho Chehab 
1495*0c0d06caSMauro Carvalho Chehab 		/* Process entity */
1496*0c0d06caSMauro Carvalho Chehab 		if (uvc_scan_chain_entity(chain, entity) < 0)
1497*0c0d06caSMauro Carvalho Chehab 			return -EINVAL;
1498*0c0d06caSMauro Carvalho Chehab 
1499*0c0d06caSMauro Carvalho Chehab 		/* Forward scan */
1500*0c0d06caSMauro Carvalho Chehab 		if (uvc_scan_chain_forward(chain, entity, prev) < 0)
1501*0c0d06caSMauro Carvalho Chehab 			return -EINVAL;
1502*0c0d06caSMauro Carvalho Chehab 
1503*0c0d06caSMauro Carvalho Chehab 		/* Backward scan */
1504*0c0d06caSMauro Carvalho Chehab 		prev = entity;
1505*0c0d06caSMauro Carvalho Chehab 		if (uvc_scan_chain_backward(chain, &entity) < 0)
1506*0c0d06caSMauro Carvalho Chehab 			return -EINVAL;
1507*0c0d06caSMauro Carvalho Chehab 	}
1508*0c0d06caSMauro Carvalho Chehab 
1509*0c0d06caSMauro Carvalho Chehab 	return 0;
1510*0c0d06caSMauro Carvalho Chehab }
1511*0c0d06caSMauro Carvalho Chehab 
1512*0c0d06caSMauro Carvalho Chehab static unsigned int uvc_print_terms(struct list_head *terms, u16 dir,
1513*0c0d06caSMauro Carvalho Chehab 		char *buffer)
1514*0c0d06caSMauro Carvalho Chehab {
1515*0c0d06caSMauro Carvalho Chehab 	struct uvc_entity *term;
1516*0c0d06caSMauro Carvalho Chehab 	unsigned int nterms = 0;
1517*0c0d06caSMauro Carvalho Chehab 	char *p = buffer;
1518*0c0d06caSMauro Carvalho Chehab 
1519*0c0d06caSMauro Carvalho Chehab 	list_for_each_entry(term, terms, chain) {
1520*0c0d06caSMauro Carvalho Chehab 		if (!UVC_ENTITY_IS_TERM(term) ||
1521*0c0d06caSMauro Carvalho Chehab 		    UVC_TERM_DIRECTION(term) != dir)
1522*0c0d06caSMauro Carvalho Chehab 			continue;
1523*0c0d06caSMauro Carvalho Chehab 
1524*0c0d06caSMauro Carvalho Chehab 		if (nterms)
1525*0c0d06caSMauro Carvalho Chehab 			p += sprintf(p, ",");
1526*0c0d06caSMauro Carvalho Chehab 		if (++nterms >= 4) {
1527*0c0d06caSMauro Carvalho Chehab 			p += sprintf(p, "...");
1528*0c0d06caSMauro Carvalho Chehab 			break;
1529*0c0d06caSMauro Carvalho Chehab 		}
1530*0c0d06caSMauro Carvalho Chehab 		p += sprintf(p, "%u", term->id);
1531*0c0d06caSMauro Carvalho Chehab 	}
1532*0c0d06caSMauro Carvalho Chehab 
1533*0c0d06caSMauro Carvalho Chehab 	return p - buffer;
1534*0c0d06caSMauro Carvalho Chehab }
1535*0c0d06caSMauro Carvalho Chehab 
1536*0c0d06caSMauro Carvalho Chehab static const char *uvc_print_chain(struct uvc_video_chain *chain)
1537*0c0d06caSMauro Carvalho Chehab {
1538*0c0d06caSMauro Carvalho Chehab 	static char buffer[43];
1539*0c0d06caSMauro Carvalho Chehab 	char *p = buffer;
1540*0c0d06caSMauro Carvalho Chehab 
1541*0c0d06caSMauro Carvalho Chehab 	p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p);
1542*0c0d06caSMauro Carvalho Chehab 	p += sprintf(p, " -> ");
1543*0c0d06caSMauro Carvalho Chehab 	uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p);
1544*0c0d06caSMauro Carvalho Chehab 
1545*0c0d06caSMauro Carvalho Chehab 	return buffer;
1546*0c0d06caSMauro Carvalho Chehab }
1547*0c0d06caSMauro Carvalho Chehab 
1548*0c0d06caSMauro Carvalho Chehab /*
1549*0c0d06caSMauro Carvalho Chehab  * Scan the device for video chains and register video devices.
1550*0c0d06caSMauro Carvalho Chehab  *
1551*0c0d06caSMauro Carvalho Chehab  * Chains are scanned starting at their output terminals and walked backwards.
1552*0c0d06caSMauro Carvalho Chehab  */
1553*0c0d06caSMauro Carvalho Chehab static int uvc_scan_device(struct uvc_device *dev)
1554*0c0d06caSMauro Carvalho Chehab {
1555*0c0d06caSMauro Carvalho Chehab 	struct uvc_video_chain *chain;
1556*0c0d06caSMauro Carvalho Chehab 	struct uvc_entity *term;
1557*0c0d06caSMauro Carvalho Chehab 
1558*0c0d06caSMauro Carvalho Chehab 	list_for_each_entry(term, &dev->entities, list) {
1559*0c0d06caSMauro Carvalho Chehab 		if (!UVC_ENTITY_IS_OTERM(term))
1560*0c0d06caSMauro Carvalho Chehab 			continue;
1561*0c0d06caSMauro Carvalho Chehab 
1562*0c0d06caSMauro Carvalho Chehab 		/* If the terminal is already included in a chain, skip it.
1563*0c0d06caSMauro Carvalho Chehab 		 * This can happen for chains that have multiple output
1564*0c0d06caSMauro Carvalho Chehab 		 * terminals, where all output terminals beside the first one
1565*0c0d06caSMauro Carvalho Chehab 		 * will be inserted in the chain in forward scans.
1566*0c0d06caSMauro Carvalho Chehab 		 */
1567*0c0d06caSMauro Carvalho Chehab 		if (term->chain.next || term->chain.prev)
1568*0c0d06caSMauro Carvalho Chehab 			continue;
1569*0c0d06caSMauro Carvalho Chehab 
1570*0c0d06caSMauro Carvalho Chehab 		chain = kzalloc(sizeof(*chain), GFP_KERNEL);
1571*0c0d06caSMauro Carvalho Chehab 		if (chain == NULL)
1572*0c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
1573*0c0d06caSMauro Carvalho Chehab 
1574*0c0d06caSMauro Carvalho Chehab 		INIT_LIST_HEAD(&chain->entities);
1575*0c0d06caSMauro Carvalho Chehab 		mutex_init(&chain->ctrl_mutex);
1576*0c0d06caSMauro Carvalho Chehab 		chain->dev = dev;
1577*0c0d06caSMauro Carvalho Chehab 
1578*0c0d06caSMauro Carvalho Chehab 		if (uvc_scan_chain(chain, term) < 0) {
1579*0c0d06caSMauro Carvalho Chehab 			kfree(chain);
1580*0c0d06caSMauro Carvalho Chehab 			continue;
1581*0c0d06caSMauro Carvalho Chehab 		}
1582*0c0d06caSMauro Carvalho Chehab 
1583*0c0d06caSMauro Carvalho Chehab 		uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",
1584*0c0d06caSMauro Carvalho Chehab 			  uvc_print_chain(chain));
1585*0c0d06caSMauro Carvalho Chehab 
1586*0c0d06caSMauro Carvalho Chehab 		list_add_tail(&chain->list, &dev->chains);
1587*0c0d06caSMauro Carvalho Chehab 	}
1588*0c0d06caSMauro Carvalho Chehab 
1589*0c0d06caSMauro Carvalho Chehab 	if (list_empty(&dev->chains)) {
1590*0c0d06caSMauro Carvalho Chehab 		uvc_printk(KERN_INFO, "No valid video chain found.\n");
1591*0c0d06caSMauro Carvalho Chehab 		return -1;
1592*0c0d06caSMauro Carvalho Chehab 	}
1593*0c0d06caSMauro Carvalho Chehab 
1594*0c0d06caSMauro Carvalho Chehab 	return 0;
1595*0c0d06caSMauro Carvalho Chehab }
1596*0c0d06caSMauro Carvalho Chehab 
1597*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
1598*0c0d06caSMauro Carvalho Chehab  * Video device registration and unregistration
1599*0c0d06caSMauro Carvalho Chehab  */
1600*0c0d06caSMauro Carvalho Chehab 
1601*0c0d06caSMauro Carvalho Chehab /*
1602*0c0d06caSMauro Carvalho Chehab  * Delete the UVC device.
1603*0c0d06caSMauro Carvalho Chehab  *
1604*0c0d06caSMauro Carvalho Chehab  * Called by the kernel when the last reference to the uvc_device structure
1605*0c0d06caSMauro Carvalho Chehab  * is released.
1606*0c0d06caSMauro Carvalho Chehab  *
1607*0c0d06caSMauro Carvalho Chehab  * As this function is called after or during disconnect(), all URBs have
1608*0c0d06caSMauro Carvalho Chehab  * already been canceled by the USB core. There is no need to kill the
1609*0c0d06caSMauro Carvalho Chehab  * interrupt URB manually.
1610*0c0d06caSMauro Carvalho Chehab  */
1611*0c0d06caSMauro Carvalho Chehab static void uvc_delete(struct uvc_device *dev)
1612*0c0d06caSMauro Carvalho Chehab {
1613*0c0d06caSMauro Carvalho Chehab 	struct list_head *p, *n;
1614*0c0d06caSMauro Carvalho Chehab 
1615*0c0d06caSMauro Carvalho Chehab 	usb_put_intf(dev->intf);
1616*0c0d06caSMauro Carvalho Chehab 	usb_put_dev(dev->udev);
1617*0c0d06caSMauro Carvalho Chehab 
1618*0c0d06caSMauro Carvalho Chehab 	uvc_status_cleanup(dev);
1619*0c0d06caSMauro Carvalho Chehab 	uvc_ctrl_cleanup_device(dev);
1620*0c0d06caSMauro Carvalho Chehab 
1621*0c0d06caSMauro Carvalho Chehab 	if (dev->vdev.dev)
1622*0c0d06caSMauro Carvalho Chehab 		v4l2_device_unregister(&dev->vdev);
1623*0c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER
1624*0c0d06caSMauro Carvalho Chehab 	if (media_devnode_is_registered(&dev->mdev.devnode))
1625*0c0d06caSMauro Carvalho Chehab 		media_device_unregister(&dev->mdev);
1626*0c0d06caSMauro Carvalho Chehab #endif
1627*0c0d06caSMauro Carvalho Chehab 
1628*0c0d06caSMauro Carvalho Chehab 	list_for_each_safe(p, n, &dev->chains) {
1629*0c0d06caSMauro Carvalho Chehab 		struct uvc_video_chain *chain;
1630*0c0d06caSMauro Carvalho Chehab 		chain = list_entry(p, struct uvc_video_chain, list);
1631*0c0d06caSMauro Carvalho Chehab 		kfree(chain);
1632*0c0d06caSMauro Carvalho Chehab 	}
1633*0c0d06caSMauro Carvalho Chehab 
1634*0c0d06caSMauro Carvalho Chehab 	list_for_each_safe(p, n, &dev->entities) {
1635*0c0d06caSMauro Carvalho Chehab 		struct uvc_entity *entity;
1636*0c0d06caSMauro Carvalho Chehab 		entity = list_entry(p, struct uvc_entity, list);
1637*0c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER
1638*0c0d06caSMauro Carvalho Chehab 		uvc_mc_cleanup_entity(entity);
1639*0c0d06caSMauro Carvalho Chehab #endif
1640*0c0d06caSMauro Carvalho Chehab 		if (entity->vdev) {
1641*0c0d06caSMauro Carvalho Chehab 			video_device_release(entity->vdev);
1642*0c0d06caSMauro Carvalho Chehab 			entity->vdev = NULL;
1643*0c0d06caSMauro Carvalho Chehab 		}
1644*0c0d06caSMauro Carvalho Chehab 		kfree(entity);
1645*0c0d06caSMauro Carvalho Chehab 	}
1646*0c0d06caSMauro Carvalho Chehab 
1647*0c0d06caSMauro Carvalho Chehab 	list_for_each_safe(p, n, &dev->streams) {
1648*0c0d06caSMauro Carvalho Chehab 		struct uvc_streaming *streaming;
1649*0c0d06caSMauro Carvalho Chehab 		streaming = list_entry(p, struct uvc_streaming, list);
1650*0c0d06caSMauro Carvalho Chehab 		usb_driver_release_interface(&uvc_driver.driver,
1651*0c0d06caSMauro Carvalho Chehab 			streaming->intf);
1652*0c0d06caSMauro Carvalho Chehab 		usb_put_intf(streaming->intf);
1653*0c0d06caSMauro Carvalho Chehab 		kfree(streaming->format);
1654*0c0d06caSMauro Carvalho Chehab 		kfree(streaming->header.bmaControls);
1655*0c0d06caSMauro Carvalho Chehab 		kfree(streaming);
1656*0c0d06caSMauro Carvalho Chehab 	}
1657*0c0d06caSMauro Carvalho Chehab 
1658*0c0d06caSMauro Carvalho Chehab 	kfree(dev);
1659*0c0d06caSMauro Carvalho Chehab }
1660*0c0d06caSMauro Carvalho Chehab 
1661*0c0d06caSMauro Carvalho Chehab static void uvc_release(struct video_device *vdev)
1662*0c0d06caSMauro Carvalho Chehab {
1663*0c0d06caSMauro Carvalho Chehab 	struct uvc_streaming *stream = video_get_drvdata(vdev);
1664*0c0d06caSMauro Carvalho Chehab 	struct uvc_device *dev = stream->dev;
1665*0c0d06caSMauro Carvalho Chehab 
1666*0c0d06caSMauro Carvalho Chehab 	/* Decrement the registered streams count and delete the device when it
1667*0c0d06caSMauro Carvalho Chehab 	 * reaches zero.
1668*0c0d06caSMauro Carvalho Chehab 	 */
1669*0c0d06caSMauro Carvalho Chehab 	if (atomic_dec_and_test(&dev->nstreams))
1670*0c0d06caSMauro Carvalho Chehab 		uvc_delete(dev);
1671*0c0d06caSMauro Carvalho Chehab }
1672*0c0d06caSMauro Carvalho Chehab 
1673*0c0d06caSMauro Carvalho Chehab /*
1674*0c0d06caSMauro Carvalho Chehab  * Unregister the video devices.
1675*0c0d06caSMauro Carvalho Chehab  */
1676*0c0d06caSMauro Carvalho Chehab static void uvc_unregister_video(struct uvc_device *dev)
1677*0c0d06caSMauro Carvalho Chehab {
1678*0c0d06caSMauro Carvalho Chehab 	struct uvc_streaming *stream;
1679*0c0d06caSMauro Carvalho Chehab 
1680*0c0d06caSMauro Carvalho Chehab 	/* Unregistering all video devices might result in uvc_delete() being
1681*0c0d06caSMauro Carvalho Chehab 	 * called from inside the loop if there's no open file handle. To avoid
1682*0c0d06caSMauro Carvalho Chehab 	 * that, increment the stream count before iterating over the streams
1683*0c0d06caSMauro Carvalho Chehab 	 * and decrement it when done.
1684*0c0d06caSMauro Carvalho Chehab 	 */
1685*0c0d06caSMauro Carvalho Chehab 	atomic_inc(&dev->nstreams);
1686*0c0d06caSMauro Carvalho Chehab 
1687*0c0d06caSMauro Carvalho Chehab 	list_for_each_entry(stream, &dev->streams, list) {
1688*0c0d06caSMauro Carvalho Chehab 		if (stream->vdev == NULL)
1689*0c0d06caSMauro Carvalho Chehab 			continue;
1690*0c0d06caSMauro Carvalho Chehab 
1691*0c0d06caSMauro Carvalho Chehab 		video_unregister_device(stream->vdev);
1692*0c0d06caSMauro Carvalho Chehab 		stream->vdev = NULL;
1693*0c0d06caSMauro Carvalho Chehab 
1694*0c0d06caSMauro Carvalho Chehab 		uvc_debugfs_cleanup_stream(stream);
1695*0c0d06caSMauro Carvalho Chehab 	}
1696*0c0d06caSMauro Carvalho Chehab 
1697*0c0d06caSMauro Carvalho Chehab 	/* Decrement the stream count and call uvc_delete explicitly if there
1698*0c0d06caSMauro Carvalho Chehab 	 * are no stream left.
1699*0c0d06caSMauro Carvalho Chehab 	 */
1700*0c0d06caSMauro Carvalho Chehab 	if (atomic_dec_and_test(&dev->nstreams))
1701*0c0d06caSMauro Carvalho Chehab 		uvc_delete(dev);
1702*0c0d06caSMauro Carvalho Chehab }
1703*0c0d06caSMauro Carvalho Chehab 
1704*0c0d06caSMauro Carvalho Chehab static int uvc_register_video(struct uvc_device *dev,
1705*0c0d06caSMauro Carvalho Chehab 		struct uvc_streaming *stream)
1706*0c0d06caSMauro Carvalho Chehab {
1707*0c0d06caSMauro Carvalho Chehab 	struct video_device *vdev;
1708*0c0d06caSMauro Carvalho Chehab 	int ret;
1709*0c0d06caSMauro Carvalho Chehab 
1710*0c0d06caSMauro Carvalho Chehab 	/* Initialize the streaming interface with default streaming
1711*0c0d06caSMauro Carvalho Chehab 	 * parameters.
1712*0c0d06caSMauro Carvalho Chehab 	 */
1713*0c0d06caSMauro Carvalho Chehab 	ret = uvc_video_init(stream);
1714*0c0d06caSMauro Carvalho Chehab 	if (ret < 0) {
1715*0c0d06caSMauro Carvalho Chehab 		uvc_printk(KERN_ERR, "Failed to initialize the device "
1716*0c0d06caSMauro Carvalho Chehab 			"(%d).\n", ret);
1717*0c0d06caSMauro Carvalho Chehab 		return ret;
1718*0c0d06caSMauro Carvalho Chehab 	}
1719*0c0d06caSMauro Carvalho Chehab 
1720*0c0d06caSMauro Carvalho Chehab 	uvc_debugfs_init_stream(stream);
1721*0c0d06caSMauro Carvalho Chehab 
1722*0c0d06caSMauro Carvalho Chehab 	/* Register the device with V4L. */
1723*0c0d06caSMauro Carvalho Chehab 	vdev = video_device_alloc();
1724*0c0d06caSMauro Carvalho Chehab 	if (vdev == NULL) {
1725*0c0d06caSMauro Carvalho Chehab 		uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n",
1726*0c0d06caSMauro Carvalho Chehab 			   ret);
1727*0c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
1728*0c0d06caSMauro Carvalho Chehab 	}
1729*0c0d06caSMauro Carvalho Chehab 
1730*0c0d06caSMauro Carvalho Chehab 	/* We already hold a reference to dev->udev. The video device will be
1731*0c0d06caSMauro Carvalho Chehab 	 * unregistered before the reference is released, so we don't need to
1732*0c0d06caSMauro Carvalho Chehab 	 * get another one.
1733*0c0d06caSMauro Carvalho Chehab 	 */
1734*0c0d06caSMauro Carvalho Chehab 	vdev->v4l2_dev = &dev->vdev;
1735*0c0d06caSMauro Carvalho Chehab 	vdev->fops = &uvc_fops;
1736*0c0d06caSMauro Carvalho Chehab 	vdev->release = uvc_release;
1737*0c0d06caSMauro Carvalho Chehab 	strlcpy(vdev->name, dev->name, sizeof vdev->name);
1738*0c0d06caSMauro Carvalho Chehab 
1739*0c0d06caSMauro Carvalho Chehab 	/* Set the driver data before calling video_register_device, otherwise
1740*0c0d06caSMauro Carvalho Chehab 	 * uvc_v4l2_open might race us.
1741*0c0d06caSMauro Carvalho Chehab 	 */
1742*0c0d06caSMauro Carvalho Chehab 	stream->vdev = vdev;
1743*0c0d06caSMauro Carvalho Chehab 	video_set_drvdata(vdev, stream);
1744*0c0d06caSMauro Carvalho Chehab 
1745*0c0d06caSMauro Carvalho Chehab 	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
1746*0c0d06caSMauro Carvalho Chehab 	if (ret < 0) {
1747*0c0d06caSMauro Carvalho Chehab 		uvc_printk(KERN_ERR, "Failed to register video device (%d).\n",
1748*0c0d06caSMauro Carvalho Chehab 			   ret);
1749*0c0d06caSMauro Carvalho Chehab 		stream->vdev = NULL;
1750*0c0d06caSMauro Carvalho Chehab 		video_device_release(vdev);
1751*0c0d06caSMauro Carvalho Chehab 		return ret;
1752*0c0d06caSMauro Carvalho Chehab 	}
1753*0c0d06caSMauro Carvalho Chehab 
1754*0c0d06caSMauro Carvalho Chehab 	atomic_inc(&dev->nstreams);
1755*0c0d06caSMauro Carvalho Chehab 	return 0;
1756*0c0d06caSMauro Carvalho Chehab }
1757*0c0d06caSMauro Carvalho Chehab 
1758*0c0d06caSMauro Carvalho Chehab /*
1759*0c0d06caSMauro Carvalho Chehab  * Register all video devices in all chains.
1760*0c0d06caSMauro Carvalho Chehab  */
1761*0c0d06caSMauro Carvalho Chehab static int uvc_register_terms(struct uvc_device *dev,
1762*0c0d06caSMauro Carvalho Chehab 	struct uvc_video_chain *chain)
1763*0c0d06caSMauro Carvalho Chehab {
1764*0c0d06caSMauro Carvalho Chehab 	struct uvc_streaming *stream;
1765*0c0d06caSMauro Carvalho Chehab 	struct uvc_entity *term;
1766*0c0d06caSMauro Carvalho Chehab 	int ret;
1767*0c0d06caSMauro Carvalho Chehab 
1768*0c0d06caSMauro Carvalho Chehab 	list_for_each_entry(term, &chain->entities, chain) {
1769*0c0d06caSMauro Carvalho Chehab 		if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
1770*0c0d06caSMauro Carvalho Chehab 			continue;
1771*0c0d06caSMauro Carvalho Chehab 
1772*0c0d06caSMauro Carvalho Chehab 		stream = uvc_stream_by_id(dev, term->id);
1773*0c0d06caSMauro Carvalho Chehab 		if (stream == NULL) {
1774*0c0d06caSMauro Carvalho Chehab 			uvc_printk(KERN_INFO, "No streaming interface found "
1775*0c0d06caSMauro Carvalho Chehab 				   "for terminal %u.", term->id);
1776*0c0d06caSMauro Carvalho Chehab 			continue;
1777*0c0d06caSMauro Carvalho Chehab 		}
1778*0c0d06caSMauro Carvalho Chehab 
1779*0c0d06caSMauro Carvalho Chehab 		stream->chain = chain;
1780*0c0d06caSMauro Carvalho Chehab 		ret = uvc_register_video(dev, stream);
1781*0c0d06caSMauro Carvalho Chehab 		if (ret < 0)
1782*0c0d06caSMauro Carvalho Chehab 			return ret;
1783*0c0d06caSMauro Carvalho Chehab 
1784*0c0d06caSMauro Carvalho Chehab 		term->vdev = stream->vdev;
1785*0c0d06caSMauro Carvalho Chehab 	}
1786*0c0d06caSMauro Carvalho Chehab 
1787*0c0d06caSMauro Carvalho Chehab 	return 0;
1788*0c0d06caSMauro Carvalho Chehab }
1789*0c0d06caSMauro Carvalho Chehab 
1790*0c0d06caSMauro Carvalho Chehab static int uvc_register_chains(struct uvc_device *dev)
1791*0c0d06caSMauro Carvalho Chehab {
1792*0c0d06caSMauro Carvalho Chehab 	struct uvc_video_chain *chain;
1793*0c0d06caSMauro Carvalho Chehab 	int ret;
1794*0c0d06caSMauro Carvalho Chehab 
1795*0c0d06caSMauro Carvalho Chehab 	list_for_each_entry(chain, &dev->chains, list) {
1796*0c0d06caSMauro Carvalho Chehab 		ret = uvc_register_terms(dev, chain);
1797*0c0d06caSMauro Carvalho Chehab 		if (ret < 0)
1798*0c0d06caSMauro Carvalho Chehab 			return ret;
1799*0c0d06caSMauro Carvalho Chehab 
1800*0c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER
1801*0c0d06caSMauro Carvalho Chehab 		ret = uvc_mc_register_entities(chain);
1802*0c0d06caSMauro Carvalho Chehab 		if (ret < 0) {
1803*0c0d06caSMauro Carvalho Chehab 			uvc_printk(KERN_INFO, "Failed to register entites "
1804*0c0d06caSMauro Carvalho Chehab 				"(%d).\n", ret);
1805*0c0d06caSMauro Carvalho Chehab 		}
1806*0c0d06caSMauro Carvalho Chehab #endif
1807*0c0d06caSMauro Carvalho Chehab 	}
1808*0c0d06caSMauro Carvalho Chehab 
1809*0c0d06caSMauro Carvalho Chehab 	return 0;
1810*0c0d06caSMauro Carvalho Chehab }
1811*0c0d06caSMauro Carvalho Chehab 
1812*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
1813*0c0d06caSMauro Carvalho Chehab  * USB probe, disconnect, suspend and resume
1814*0c0d06caSMauro Carvalho Chehab  */
1815*0c0d06caSMauro Carvalho Chehab 
1816*0c0d06caSMauro Carvalho Chehab static int uvc_probe(struct usb_interface *intf,
1817*0c0d06caSMauro Carvalho Chehab 		     const struct usb_device_id *id)
1818*0c0d06caSMauro Carvalho Chehab {
1819*0c0d06caSMauro Carvalho Chehab 	struct usb_device *udev = interface_to_usbdev(intf);
1820*0c0d06caSMauro Carvalho Chehab 	struct uvc_device *dev;
1821*0c0d06caSMauro Carvalho Chehab 	int ret;
1822*0c0d06caSMauro Carvalho Chehab 
1823*0c0d06caSMauro Carvalho Chehab 	if (id->idVendor && id->idProduct)
1824*0c0d06caSMauro Carvalho Chehab 		uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
1825*0c0d06caSMauro Carvalho Chehab 				"(%04x:%04x)\n", udev->devpath, id->idVendor,
1826*0c0d06caSMauro Carvalho Chehab 				id->idProduct);
1827*0c0d06caSMauro Carvalho Chehab 	else
1828*0c0d06caSMauro Carvalho Chehab 		uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
1829*0c0d06caSMauro Carvalho Chehab 				udev->devpath);
1830*0c0d06caSMauro Carvalho Chehab 
1831*0c0d06caSMauro Carvalho Chehab 	/* Allocate memory for the device and initialize it. */
1832*0c0d06caSMauro Carvalho Chehab 	if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL)
1833*0c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
1834*0c0d06caSMauro Carvalho Chehab 
1835*0c0d06caSMauro Carvalho Chehab 	INIT_LIST_HEAD(&dev->entities);
1836*0c0d06caSMauro Carvalho Chehab 	INIT_LIST_HEAD(&dev->chains);
1837*0c0d06caSMauro Carvalho Chehab 	INIT_LIST_HEAD(&dev->streams);
1838*0c0d06caSMauro Carvalho Chehab 	atomic_set(&dev->nstreams, 0);
1839*0c0d06caSMauro Carvalho Chehab 	atomic_set(&dev->users, 0);
1840*0c0d06caSMauro Carvalho Chehab 	atomic_set(&dev->nmappings, 0);
1841*0c0d06caSMauro Carvalho Chehab 
1842*0c0d06caSMauro Carvalho Chehab 	dev->udev = usb_get_dev(udev);
1843*0c0d06caSMauro Carvalho Chehab 	dev->intf = usb_get_intf(intf);
1844*0c0d06caSMauro Carvalho Chehab 	dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
1845*0c0d06caSMauro Carvalho Chehab 	dev->quirks = (uvc_quirks_param == -1)
1846*0c0d06caSMauro Carvalho Chehab 		    ? id->driver_info : uvc_quirks_param;
1847*0c0d06caSMauro Carvalho Chehab 
1848*0c0d06caSMauro Carvalho Chehab 	if (udev->product != NULL)
1849*0c0d06caSMauro Carvalho Chehab 		strlcpy(dev->name, udev->product, sizeof dev->name);
1850*0c0d06caSMauro Carvalho Chehab 	else
1851*0c0d06caSMauro Carvalho Chehab 		snprintf(dev->name, sizeof dev->name,
1852*0c0d06caSMauro Carvalho Chehab 			"UVC Camera (%04x:%04x)",
1853*0c0d06caSMauro Carvalho Chehab 			le16_to_cpu(udev->descriptor.idVendor),
1854*0c0d06caSMauro Carvalho Chehab 			le16_to_cpu(udev->descriptor.idProduct));
1855*0c0d06caSMauro Carvalho Chehab 
1856*0c0d06caSMauro Carvalho Chehab 	/* Parse the Video Class control descriptor. */
1857*0c0d06caSMauro Carvalho Chehab 	if (uvc_parse_control(dev) < 0) {
1858*0c0d06caSMauro Carvalho Chehab 		uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC "
1859*0c0d06caSMauro Carvalho Chehab 			"descriptors.\n");
1860*0c0d06caSMauro Carvalho Chehab 		goto error;
1861*0c0d06caSMauro Carvalho Chehab 	}
1862*0c0d06caSMauro Carvalho Chehab 
1863*0c0d06caSMauro Carvalho Chehab 	uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n",
1864*0c0d06caSMauro Carvalho Chehab 		dev->uvc_version >> 8, dev->uvc_version & 0xff,
1865*0c0d06caSMauro Carvalho Chehab 		udev->product ? udev->product : "<unnamed>",
1866*0c0d06caSMauro Carvalho Chehab 		le16_to_cpu(udev->descriptor.idVendor),
1867*0c0d06caSMauro Carvalho Chehab 		le16_to_cpu(udev->descriptor.idProduct));
1868*0c0d06caSMauro Carvalho Chehab 
1869*0c0d06caSMauro Carvalho Chehab 	if (dev->quirks != id->driver_info) {
1870*0c0d06caSMauro Carvalho Chehab 		uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module "
1871*0c0d06caSMauro Carvalho Chehab 			"parameter for testing purpose.\n", dev->quirks);
1872*0c0d06caSMauro Carvalho Chehab 		uvc_printk(KERN_INFO, "Please report required quirks to the "
1873*0c0d06caSMauro Carvalho Chehab 			"linux-uvc-devel mailing list.\n");
1874*0c0d06caSMauro Carvalho Chehab 	}
1875*0c0d06caSMauro Carvalho Chehab 
1876*0c0d06caSMauro Carvalho Chehab 	/* Register the media and V4L2 devices. */
1877*0c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER
1878*0c0d06caSMauro Carvalho Chehab 	dev->mdev.dev = &intf->dev;
1879*0c0d06caSMauro Carvalho Chehab 	strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
1880*0c0d06caSMauro Carvalho Chehab 	if (udev->serial)
1881*0c0d06caSMauro Carvalho Chehab 		strlcpy(dev->mdev.serial, udev->serial,
1882*0c0d06caSMauro Carvalho Chehab 			sizeof(dev->mdev.serial));
1883*0c0d06caSMauro Carvalho Chehab 	strcpy(dev->mdev.bus_info, udev->devpath);
1884*0c0d06caSMauro Carvalho Chehab 	dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
1885*0c0d06caSMauro Carvalho Chehab 	dev->mdev.driver_version = LINUX_VERSION_CODE;
1886*0c0d06caSMauro Carvalho Chehab 	if (media_device_register(&dev->mdev) < 0)
1887*0c0d06caSMauro Carvalho Chehab 		goto error;
1888*0c0d06caSMauro Carvalho Chehab 
1889*0c0d06caSMauro Carvalho Chehab 	dev->vdev.mdev = &dev->mdev;
1890*0c0d06caSMauro Carvalho Chehab #endif
1891*0c0d06caSMauro Carvalho Chehab 	if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
1892*0c0d06caSMauro Carvalho Chehab 		goto error;
1893*0c0d06caSMauro Carvalho Chehab 
1894*0c0d06caSMauro Carvalho Chehab 	/* Initialize controls. */
1895*0c0d06caSMauro Carvalho Chehab 	if (uvc_ctrl_init_device(dev) < 0)
1896*0c0d06caSMauro Carvalho Chehab 		goto error;
1897*0c0d06caSMauro Carvalho Chehab 
1898*0c0d06caSMauro Carvalho Chehab 	/* Scan the device for video chains. */
1899*0c0d06caSMauro Carvalho Chehab 	if (uvc_scan_device(dev) < 0)
1900*0c0d06caSMauro Carvalho Chehab 		goto error;
1901*0c0d06caSMauro Carvalho Chehab 
1902*0c0d06caSMauro Carvalho Chehab 	/* Register video device nodes. */
1903*0c0d06caSMauro Carvalho Chehab 	if (uvc_register_chains(dev) < 0)
1904*0c0d06caSMauro Carvalho Chehab 		goto error;
1905*0c0d06caSMauro Carvalho Chehab 
1906*0c0d06caSMauro Carvalho Chehab 	/* Save our data pointer in the interface data. */
1907*0c0d06caSMauro Carvalho Chehab 	usb_set_intfdata(intf, dev);
1908*0c0d06caSMauro Carvalho Chehab 
1909*0c0d06caSMauro Carvalho Chehab 	/* Initialize the interrupt URB. */
1910*0c0d06caSMauro Carvalho Chehab 	if ((ret = uvc_status_init(dev)) < 0) {
1911*0c0d06caSMauro Carvalho Chehab 		uvc_printk(KERN_INFO, "Unable to initialize the status "
1912*0c0d06caSMauro Carvalho Chehab 			"endpoint (%d), status interrupt will not be "
1913*0c0d06caSMauro Carvalho Chehab 			"supported.\n", ret);
1914*0c0d06caSMauro Carvalho Chehab 	}
1915*0c0d06caSMauro Carvalho Chehab 
1916*0c0d06caSMauro Carvalho Chehab 	uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
1917*0c0d06caSMauro Carvalho Chehab 	usb_enable_autosuspend(udev);
1918*0c0d06caSMauro Carvalho Chehab 	return 0;
1919*0c0d06caSMauro Carvalho Chehab 
1920*0c0d06caSMauro Carvalho Chehab error:
1921*0c0d06caSMauro Carvalho Chehab 	uvc_unregister_video(dev);
1922*0c0d06caSMauro Carvalho Chehab 	return -ENODEV;
1923*0c0d06caSMauro Carvalho Chehab }
1924*0c0d06caSMauro Carvalho Chehab 
1925*0c0d06caSMauro Carvalho Chehab static void uvc_disconnect(struct usb_interface *intf)
1926*0c0d06caSMauro Carvalho Chehab {
1927*0c0d06caSMauro Carvalho Chehab 	struct uvc_device *dev = usb_get_intfdata(intf);
1928*0c0d06caSMauro Carvalho Chehab 
1929*0c0d06caSMauro Carvalho Chehab 	/* Set the USB interface data to NULL. This can be done outside the
1930*0c0d06caSMauro Carvalho Chehab 	 * lock, as there's no other reader.
1931*0c0d06caSMauro Carvalho Chehab 	 */
1932*0c0d06caSMauro Carvalho Chehab 	usb_set_intfdata(intf, NULL);
1933*0c0d06caSMauro Carvalho Chehab 
1934*0c0d06caSMauro Carvalho Chehab 	if (intf->cur_altsetting->desc.bInterfaceSubClass ==
1935*0c0d06caSMauro Carvalho Chehab 	    UVC_SC_VIDEOSTREAMING)
1936*0c0d06caSMauro Carvalho Chehab 		return;
1937*0c0d06caSMauro Carvalho Chehab 
1938*0c0d06caSMauro Carvalho Chehab 	dev->state |= UVC_DEV_DISCONNECTED;
1939*0c0d06caSMauro Carvalho Chehab 
1940*0c0d06caSMauro Carvalho Chehab 	uvc_unregister_video(dev);
1941*0c0d06caSMauro Carvalho Chehab }
1942*0c0d06caSMauro Carvalho Chehab 
1943*0c0d06caSMauro Carvalho Chehab static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
1944*0c0d06caSMauro Carvalho Chehab {
1945*0c0d06caSMauro Carvalho Chehab 	struct uvc_device *dev = usb_get_intfdata(intf);
1946*0c0d06caSMauro Carvalho Chehab 	struct uvc_streaming *stream;
1947*0c0d06caSMauro Carvalho Chehab 
1948*0c0d06caSMauro Carvalho Chehab 	uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n",
1949*0c0d06caSMauro Carvalho Chehab 		intf->cur_altsetting->desc.bInterfaceNumber);
1950*0c0d06caSMauro Carvalho Chehab 
1951*0c0d06caSMauro Carvalho Chehab 	/* Controls are cached on the fly so they don't need to be saved. */
1952*0c0d06caSMauro Carvalho Chehab 	if (intf->cur_altsetting->desc.bInterfaceSubClass ==
1953*0c0d06caSMauro Carvalho Chehab 	    UVC_SC_VIDEOCONTROL)
1954*0c0d06caSMauro Carvalho Chehab 		return uvc_status_suspend(dev);
1955*0c0d06caSMauro Carvalho Chehab 
1956*0c0d06caSMauro Carvalho Chehab 	list_for_each_entry(stream, &dev->streams, list) {
1957*0c0d06caSMauro Carvalho Chehab 		if (stream->intf == intf)
1958*0c0d06caSMauro Carvalho Chehab 			return uvc_video_suspend(stream);
1959*0c0d06caSMauro Carvalho Chehab 	}
1960*0c0d06caSMauro Carvalho Chehab 
1961*0c0d06caSMauro Carvalho Chehab 	uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface "
1962*0c0d06caSMauro Carvalho Chehab 			"mismatch.\n");
1963*0c0d06caSMauro Carvalho Chehab 	return -EINVAL;
1964*0c0d06caSMauro Carvalho Chehab }
1965*0c0d06caSMauro Carvalho Chehab 
1966*0c0d06caSMauro Carvalho Chehab static int __uvc_resume(struct usb_interface *intf, int reset)
1967*0c0d06caSMauro Carvalho Chehab {
1968*0c0d06caSMauro Carvalho Chehab 	struct uvc_device *dev = usb_get_intfdata(intf);
1969*0c0d06caSMauro Carvalho Chehab 	struct uvc_streaming *stream;
1970*0c0d06caSMauro Carvalho Chehab 
1971*0c0d06caSMauro Carvalho Chehab 	uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n",
1972*0c0d06caSMauro Carvalho Chehab 		intf->cur_altsetting->desc.bInterfaceNumber);
1973*0c0d06caSMauro Carvalho Chehab 
1974*0c0d06caSMauro Carvalho Chehab 	if (intf->cur_altsetting->desc.bInterfaceSubClass ==
1975*0c0d06caSMauro Carvalho Chehab 	    UVC_SC_VIDEOCONTROL) {
1976*0c0d06caSMauro Carvalho Chehab 		if (reset) {
1977*0c0d06caSMauro Carvalho Chehab 			int ret = uvc_ctrl_resume_device(dev);
1978*0c0d06caSMauro Carvalho Chehab 
1979*0c0d06caSMauro Carvalho Chehab 			if (ret < 0)
1980*0c0d06caSMauro Carvalho Chehab 				return ret;
1981*0c0d06caSMauro Carvalho Chehab 		}
1982*0c0d06caSMauro Carvalho Chehab 
1983*0c0d06caSMauro Carvalho Chehab 		return uvc_status_resume(dev);
1984*0c0d06caSMauro Carvalho Chehab 	}
1985*0c0d06caSMauro Carvalho Chehab 
1986*0c0d06caSMauro Carvalho Chehab 	list_for_each_entry(stream, &dev->streams, list) {
1987*0c0d06caSMauro Carvalho Chehab 		if (stream->intf == intf)
1988*0c0d06caSMauro Carvalho Chehab 			return uvc_video_resume(stream, reset);
1989*0c0d06caSMauro Carvalho Chehab 	}
1990*0c0d06caSMauro Carvalho Chehab 
1991*0c0d06caSMauro Carvalho Chehab 	uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface "
1992*0c0d06caSMauro Carvalho Chehab 			"mismatch.\n");
1993*0c0d06caSMauro Carvalho Chehab 	return -EINVAL;
1994*0c0d06caSMauro Carvalho Chehab }
1995*0c0d06caSMauro Carvalho Chehab 
1996*0c0d06caSMauro Carvalho Chehab static int uvc_resume(struct usb_interface *intf)
1997*0c0d06caSMauro Carvalho Chehab {
1998*0c0d06caSMauro Carvalho Chehab 	return __uvc_resume(intf, 0);
1999*0c0d06caSMauro Carvalho Chehab }
2000*0c0d06caSMauro Carvalho Chehab 
2001*0c0d06caSMauro Carvalho Chehab static int uvc_reset_resume(struct usb_interface *intf)
2002*0c0d06caSMauro Carvalho Chehab {
2003*0c0d06caSMauro Carvalho Chehab 	return __uvc_resume(intf, 1);
2004*0c0d06caSMauro Carvalho Chehab }
2005*0c0d06caSMauro Carvalho Chehab 
2006*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
2007*0c0d06caSMauro Carvalho Chehab  * Module parameters
2008*0c0d06caSMauro Carvalho Chehab  */
2009*0c0d06caSMauro Carvalho Chehab 
2010*0c0d06caSMauro Carvalho Chehab static int uvc_clock_param_get(char *buffer, struct kernel_param *kp)
2011*0c0d06caSMauro Carvalho Chehab {
2012*0c0d06caSMauro Carvalho Chehab 	if (uvc_clock_param == CLOCK_MONOTONIC)
2013*0c0d06caSMauro Carvalho Chehab 		return sprintf(buffer, "CLOCK_MONOTONIC");
2014*0c0d06caSMauro Carvalho Chehab 	else
2015*0c0d06caSMauro Carvalho Chehab 		return sprintf(buffer, "CLOCK_REALTIME");
2016*0c0d06caSMauro Carvalho Chehab }
2017*0c0d06caSMauro Carvalho Chehab 
2018*0c0d06caSMauro Carvalho Chehab static int uvc_clock_param_set(const char *val, struct kernel_param *kp)
2019*0c0d06caSMauro Carvalho Chehab {
2020*0c0d06caSMauro Carvalho Chehab 	if (strncasecmp(val, "clock_", strlen("clock_")) == 0)
2021*0c0d06caSMauro Carvalho Chehab 		val += strlen("clock_");
2022*0c0d06caSMauro Carvalho Chehab 
2023*0c0d06caSMauro Carvalho Chehab 	if (strcasecmp(val, "monotonic") == 0)
2024*0c0d06caSMauro Carvalho Chehab 		uvc_clock_param = CLOCK_MONOTONIC;
2025*0c0d06caSMauro Carvalho Chehab 	else if (strcasecmp(val, "realtime") == 0)
2026*0c0d06caSMauro Carvalho Chehab 		uvc_clock_param = CLOCK_REALTIME;
2027*0c0d06caSMauro Carvalho Chehab 	else
2028*0c0d06caSMauro Carvalho Chehab 		return -EINVAL;
2029*0c0d06caSMauro Carvalho Chehab 
2030*0c0d06caSMauro Carvalho Chehab 	return 0;
2031*0c0d06caSMauro Carvalho Chehab }
2032*0c0d06caSMauro Carvalho Chehab 
2033*0c0d06caSMauro Carvalho Chehab module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get,
2034*0c0d06caSMauro Carvalho Chehab 		  &uvc_clock_param, S_IRUGO|S_IWUSR);
2035*0c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(clock, "Video buffers timestamp clock");
2036*0c0d06caSMauro Carvalho Chehab module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR);
2037*0c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
2038*0c0d06caSMauro Carvalho Chehab module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
2039*0c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(quirks, "Forced device quirks");
2040*0c0d06caSMauro Carvalho Chehab module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);
2041*0c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(trace, "Trace level bitmask");
2042*0c0d06caSMauro Carvalho Chehab module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR);
2043*0c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
2044*0c0d06caSMauro Carvalho Chehab 
2045*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
2046*0c0d06caSMauro Carvalho Chehab  * Driver initialization and cleanup
2047*0c0d06caSMauro Carvalho Chehab  */
2048*0c0d06caSMauro Carvalho Chehab 
2049*0c0d06caSMauro Carvalho Chehab /*
2050*0c0d06caSMauro Carvalho Chehab  * The Logitech cameras listed below have their interface class set to
2051*0c0d06caSMauro Carvalho Chehab  * VENDOR_SPEC because they don't announce themselves as UVC devices, even
2052*0c0d06caSMauro Carvalho Chehab  * though they are compliant.
2053*0c0d06caSMauro Carvalho Chehab  */
2054*0c0d06caSMauro Carvalho Chehab static struct usb_device_id uvc_ids[] = {
2055*0c0d06caSMauro Carvalho Chehab 	/* LogiLink Wireless Webcam */
2056*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2057*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2058*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x0416,
2059*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0xa91a,
2060*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2061*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2062*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2063*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
2064*0c0d06caSMauro Carvalho Chehab 	/* Genius eFace 2025 */
2065*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2066*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2067*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x0458,
2068*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x706e,
2069*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2070*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2071*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2072*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
2073*0c0d06caSMauro Carvalho Chehab 	/* Microsoft Lifecam NX-6000 */
2074*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2075*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2076*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x045e,
2077*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x00f8,
2078*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2079*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2080*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2081*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
2082*0c0d06caSMauro Carvalho Chehab 	/* Microsoft Lifecam VX-7000 */
2083*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2084*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2085*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x045e,
2086*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x0723,
2087*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2088*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2089*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2090*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
2091*0c0d06caSMauro Carvalho Chehab 	/* Logitech Quickcam Fusion */
2092*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2093*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2094*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x046d,
2095*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x08c1,
2096*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
2097*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2098*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0 },
2099*0c0d06caSMauro Carvalho Chehab 	/* Logitech Quickcam Orbit MP */
2100*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2101*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2102*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x046d,
2103*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x08c2,
2104*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
2105*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2106*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0 },
2107*0c0d06caSMauro Carvalho Chehab 	/* Logitech Quickcam Pro for Notebook */
2108*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2109*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2110*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x046d,
2111*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x08c3,
2112*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
2113*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2114*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0 },
2115*0c0d06caSMauro Carvalho Chehab 	/* Logitech Quickcam Pro 5000 */
2116*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2117*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2118*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x046d,
2119*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x08c5,
2120*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
2121*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2122*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0 },
2123*0c0d06caSMauro Carvalho Chehab 	/* Logitech Quickcam OEM Dell Notebook */
2124*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2125*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2126*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x046d,
2127*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x08c6,
2128*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
2129*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2130*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0 },
2131*0c0d06caSMauro Carvalho Chehab 	/* Logitech Quickcam OEM Cisco VT Camera II */
2132*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2133*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2134*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x046d,
2135*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x08c7,
2136*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
2137*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2138*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0 },
2139*0c0d06caSMauro Carvalho Chehab 	/* Chicony CNF7129 (Asus EEE 100HE) */
2140*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2141*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2142*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x04f2,
2143*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0xb071,
2144*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2145*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2146*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2147*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_RESTRICT_FRAME_RATE },
2148*0c0d06caSMauro Carvalho Chehab 	/* Alcor Micro AU3820 (Future Boy PC USB Webcam) */
2149*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2150*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2151*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x058f,
2152*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x3820,
2153*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2154*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2155*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2156*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
2157*0c0d06caSMauro Carvalho Chehab 	/* Dell XPS m1530 */
2158*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2159*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2160*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x05a9,
2161*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x2640,
2162*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2163*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2164*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2165*0c0d06caSMauro Carvalho Chehab 	  .driver_info 		= UVC_QUIRK_PROBE_DEF },
2166*0c0d06caSMauro Carvalho Chehab 	/* Apple Built-In iSight */
2167*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2168*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2169*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x05ac,
2170*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x8501,
2171*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2172*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2173*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2174*0c0d06caSMauro Carvalho Chehab 	  .driver_info 		= UVC_QUIRK_PROBE_MINMAX
2175*0c0d06caSMauro Carvalho Chehab 				| UVC_QUIRK_BUILTIN_ISIGHT },
2176*0c0d06caSMauro Carvalho Chehab 	/* Foxlink ("HP Webcam" on HP Mini 5103) */
2177*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2178*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2179*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x05c8,
2180*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x0403,
2181*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2182*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2183*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2184*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_FIX_BANDWIDTH },
2185*0c0d06caSMauro Carvalho Chehab 	/* Genesys Logic USB 2.0 PC Camera */
2186*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2187*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2188*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x05e3,
2189*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x0505,
2190*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2191*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2192*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2193*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
2194*0c0d06caSMauro Carvalho Chehab 	/* Hercules Classic Silver */
2195*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2196*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2197*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x06f8,
2198*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x300c,
2199*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2200*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2201*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2202*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_FIX_BANDWIDTH },
2203*0c0d06caSMauro Carvalho Chehab 	/* ViMicro Vega */
2204*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2205*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2206*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x0ac8,
2207*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x332d,
2208*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2209*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2210*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2211*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_FIX_BANDWIDTH },
2212*0c0d06caSMauro Carvalho Chehab 	/* ViMicro - Minoru3D */
2213*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2214*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2215*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x0ac8,
2216*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x3410,
2217*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2218*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2219*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2220*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_FIX_BANDWIDTH },
2221*0c0d06caSMauro Carvalho Chehab 	/* ViMicro Venus - Minoru3D */
2222*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2223*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2224*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x0ac8,
2225*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x3420,
2226*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2227*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2228*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2229*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_FIX_BANDWIDTH },
2230*0c0d06caSMauro Carvalho Chehab 	/* Ophir Optronics - SPCAM 620U */
2231*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2232*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2233*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x0bd3,
2234*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x0555,
2235*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2236*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2237*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2238*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
2239*0c0d06caSMauro Carvalho Chehab 	/* MT6227 */
2240*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2241*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2242*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x0e8d,
2243*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x0004,
2244*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2245*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2246*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2247*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_PROBE_MINMAX
2248*0c0d06caSMauro Carvalho Chehab 				| UVC_QUIRK_PROBE_DEF },
2249*0c0d06caSMauro Carvalho Chehab 	/* IMC Networks (Medion Akoya) */
2250*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2251*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2252*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x13d3,
2253*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x5103,
2254*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2255*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2256*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2257*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
2258*0c0d06caSMauro Carvalho Chehab 	/* JMicron USB2.0 XGA WebCam */
2259*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2260*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2261*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x152d,
2262*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x0310,
2263*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2264*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2265*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2266*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
2267*0c0d06caSMauro Carvalho Chehab 	/* Syntek (HP Spartan) */
2268*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2269*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2270*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x174f,
2271*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x5212,
2272*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2273*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2274*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2275*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
2276*0c0d06caSMauro Carvalho Chehab 	/* Syntek (Samsung Q310) */
2277*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2278*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2279*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x174f,
2280*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x5931,
2281*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2282*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2283*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2284*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
2285*0c0d06caSMauro Carvalho Chehab 	/* Syntek (Packard Bell EasyNote MX52 */
2286*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2287*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2288*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x174f,
2289*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x8a12,
2290*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2291*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2292*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2293*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
2294*0c0d06caSMauro Carvalho Chehab 	/* Syntek (Asus F9SG) */
2295*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2296*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2297*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x174f,
2298*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x8a31,
2299*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2300*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2301*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2302*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
2303*0c0d06caSMauro Carvalho Chehab 	/* Syntek (Asus U3S) */
2304*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2305*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2306*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x174f,
2307*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x8a33,
2308*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2309*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2310*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2311*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
2312*0c0d06caSMauro Carvalho Chehab 	/* Syntek (JAOtech Smart Terminal) */
2313*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2314*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2315*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x174f,
2316*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x8a34,
2317*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2318*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2319*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2320*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
2321*0c0d06caSMauro Carvalho Chehab 	/* Miricle 307K */
2322*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2323*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2324*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x17dc,
2325*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x0202,
2326*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2327*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2328*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2329*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
2330*0c0d06caSMauro Carvalho Chehab 	/* Lenovo Thinkpad SL400/SL500 */
2331*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2332*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2333*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x17ef,
2334*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x480b,
2335*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2336*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2337*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2338*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_STREAM_NO_FID },
2339*0c0d06caSMauro Carvalho Chehab 	/* Aveo Technology USB 2.0 Camera */
2340*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2341*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2342*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x1871,
2343*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x0306,
2344*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2345*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2346*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2347*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_PROBE_MINMAX
2348*0c0d06caSMauro Carvalho Chehab 				| UVC_QUIRK_PROBE_EXTRAFIELDS },
2349*0c0d06caSMauro Carvalho Chehab 	/* Ecamm Pico iMage */
2350*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2351*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2352*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x18cd,
2353*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0xcafe,
2354*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2355*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2356*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2357*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_PROBE_EXTRAFIELDS },
2358*0c0d06caSMauro Carvalho Chehab 	/* Manta MM-353 Plako */
2359*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2360*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2361*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x18ec,
2362*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x3188,
2363*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2364*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2365*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2366*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
2367*0c0d06caSMauro Carvalho Chehab 	/* FSC WebCam V30S */
2368*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2369*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2370*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x18ec,
2371*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x3288,
2372*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2373*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2374*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2375*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
2376*0c0d06caSMauro Carvalho Chehab 	/* Arkmicro unbranded */
2377*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2378*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2379*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x18ec,
2380*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x3290,
2381*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2382*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2383*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2384*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_PROBE_DEF },
2385*0c0d06caSMauro Carvalho Chehab 	/* The Imaging Source USB CCD cameras */
2386*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2387*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2388*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x199e,
2389*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x8102,
2390*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
2391*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2392*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0 },
2393*0c0d06caSMauro Carvalho Chehab 	/* Bodelin ProScopeHR */
2394*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2395*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_DEV_HI
2396*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2397*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x19ab,
2398*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x1000,
2399*0c0d06caSMauro Carvalho Chehab 	  .bcdDevice_hi		= 0x0126,
2400*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2401*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2402*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2403*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_STATUS_INTERVAL },
2404*0c0d06caSMauro Carvalho Chehab 	/* MSI StarCam 370i */
2405*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2406*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2407*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x1b3b,
2408*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x2951,
2409*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2410*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2411*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2412*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_PROBE_MINMAX },
2413*0c0d06caSMauro Carvalho Chehab 	/* SiGma Micro USB Web Camera */
2414*0c0d06caSMauro Carvalho Chehab 	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
2415*0c0d06caSMauro Carvalho Chehab 				| USB_DEVICE_ID_MATCH_INT_INFO,
2416*0c0d06caSMauro Carvalho Chehab 	  .idVendor		= 0x1c4f,
2417*0c0d06caSMauro Carvalho Chehab 	  .idProduct		= 0x3000,
2418*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceClass	= USB_CLASS_VIDEO,
2419*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceSubClass	= 1,
2420*0c0d06caSMauro Carvalho Chehab 	  .bInterfaceProtocol	= 0,
2421*0c0d06caSMauro Carvalho Chehab 	  .driver_info		= UVC_QUIRK_PROBE_MINMAX
2422*0c0d06caSMauro Carvalho Chehab 				| UVC_QUIRK_IGNORE_SELECTOR_UNIT },
2423*0c0d06caSMauro Carvalho Chehab 	/* Generic USB Video Class */
2424*0c0d06caSMauro Carvalho Chehab 	{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
2425*0c0d06caSMauro Carvalho Chehab 	{}
2426*0c0d06caSMauro Carvalho Chehab };
2427*0c0d06caSMauro Carvalho Chehab 
2428*0c0d06caSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, uvc_ids);
2429*0c0d06caSMauro Carvalho Chehab 
2430*0c0d06caSMauro Carvalho Chehab struct uvc_driver uvc_driver = {
2431*0c0d06caSMauro Carvalho Chehab 	.driver = {
2432*0c0d06caSMauro Carvalho Chehab 		.name		= "uvcvideo",
2433*0c0d06caSMauro Carvalho Chehab 		.probe		= uvc_probe,
2434*0c0d06caSMauro Carvalho Chehab 		.disconnect	= uvc_disconnect,
2435*0c0d06caSMauro Carvalho Chehab 		.suspend	= uvc_suspend,
2436*0c0d06caSMauro Carvalho Chehab 		.resume		= uvc_resume,
2437*0c0d06caSMauro Carvalho Chehab 		.reset_resume	= uvc_reset_resume,
2438*0c0d06caSMauro Carvalho Chehab 		.id_table	= uvc_ids,
2439*0c0d06caSMauro Carvalho Chehab 		.supports_autosuspend = 1,
2440*0c0d06caSMauro Carvalho Chehab 	},
2441*0c0d06caSMauro Carvalho Chehab };
2442*0c0d06caSMauro Carvalho Chehab 
2443*0c0d06caSMauro Carvalho Chehab static int __init uvc_init(void)
2444*0c0d06caSMauro Carvalho Chehab {
2445*0c0d06caSMauro Carvalho Chehab 	int ret;
2446*0c0d06caSMauro Carvalho Chehab 
2447*0c0d06caSMauro Carvalho Chehab 	uvc_debugfs_init();
2448*0c0d06caSMauro Carvalho Chehab 
2449*0c0d06caSMauro Carvalho Chehab 	ret = usb_register(&uvc_driver.driver);
2450*0c0d06caSMauro Carvalho Chehab 	if (ret < 0) {
2451*0c0d06caSMauro Carvalho Chehab 		uvc_debugfs_cleanup();
2452*0c0d06caSMauro Carvalho Chehab 		return ret;
2453*0c0d06caSMauro Carvalho Chehab 	}
2454*0c0d06caSMauro Carvalho Chehab 
2455*0c0d06caSMauro Carvalho Chehab 	printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
2456*0c0d06caSMauro Carvalho Chehab 	return 0;
2457*0c0d06caSMauro Carvalho Chehab }
2458*0c0d06caSMauro Carvalho Chehab 
2459*0c0d06caSMauro Carvalho Chehab static void __exit uvc_cleanup(void)
2460*0c0d06caSMauro Carvalho Chehab {
2461*0c0d06caSMauro Carvalho Chehab 	usb_deregister(&uvc_driver.driver);
2462*0c0d06caSMauro Carvalho Chehab 	uvc_debugfs_cleanup();
2463*0c0d06caSMauro Carvalho Chehab }
2464*0c0d06caSMauro Carvalho Chehab 
2465*0c0d06caSMauro Carvalho Chehab module_init(uvc_init);
2466*0c0d06caSMauro Carvalho Chehab module_exit(uvc_cleanup);
2467*0c0d06caSMauro Carvalho Chehab 
2468*0c0d06caSMauro Carvalho Chehab MODULE_AUTHOR(DRIVER_AUTHOR);
2469*0c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION(DRIVER_DESC);
2470*0c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL");
2471*0c0d06caSMauro Carvalho Chehab MODULE_VERSION(DRIVER_VERSION);
2472*0c0d06caSMauro Carvalho Chehab 
2473