12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab * uvc_driver.c -- USB Video Class driver
40c0d06caSMauro Carvalho Chehab *
50c0d06caSMauro Carvalho Chehab * Copyright (C) 2005-2010
60c0d06caSMauro Carvalho Chehab * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
70c0d06caSMauro Carvalho Chehab */
80c0d06caSMauro Carvalho Chehab
90c0d06caSMauro Carvalho Chehab #include <linux/atomic.h>
1040140edaSRicardo Ribalda #include <linux/bits.h>
112886477fSRicardo Ribalda #include <linux/gpio/consumer.h>
120c0d06caSMauro Carvalho Chehab #include <linux/kernel.h>
130c0d06caSMauro Carvalho Chehab #include <linux/list.h>
140c0d06caSMauro Carvalho Chehab #include <linux/module.h>
150c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
160c0d06caSMauro Carvalho Chehab #include <linux/usb.h>
174b3421c2SRicardo Ribalda #include <linux/usb/quirks.h>
18e1d5d71dSMichael Grzeschik #include <linux/usb/uvc.h>
190c0d06caSMauro Carvalho Chehab #include <linux/videodev2.h>
200c0d06caSMauro Carvalho Chehab #include <linux/vmalloc.h>
210c0d06caSMauro Carvalho Chehab #include <linux/wait.h>
220c0d06caSMauro Carvalho Chehab #include <asm/unaligned.h>
230c0d06caSMauro Carvalho Chehab
240c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h>
2531a96f4cSLaurent Pinchart #include <media/v4l2-ioctl.h>
260c0d06caSMauro Carvalho Chehab
270c0d06caSMauro Carvalho Chehab #include "uvcvideo.h"
280c0d06caSMauro Carvalho Chehab
290c0d06caSMauro Carvalho Chehab #define DRIVER_AUTHOR "Laurent Pinchart " \
300c0d06caSMauro Carvalho Chehab "<laurent.pinchart@ideasonboard.com>"
310c0d06caSMauro Carvalho Chehab #define DRIVER_DESC "USB Video Class driver"
320c0d06caSMauro Carvalho Chehab
330c0d06caSMauro Carvalho Chehab unsigned int uvc_clock_param = CLOCK_MONOTONIC;
345d0fd3c8SLaurent Pinchart unsigned int uvc_hw_timestamps_param;
350c0d06caSMauro Carvalho Chehab unsigned int uvc_no_drop_param;
360c0d06caSMauro Carvalho Chehab static unsigned int uvc_quirks_param = -1;
379e56380aSJoe Perches unsigned int uvc_dbg_param;
380c0d06caSMauro Carvalho Chehab unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
390c0d06caSMauro Carvalho Chehab
400c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
410c0d06caSMauro Carvalho Chehab * Utility functions
420c0d06caSMauro Carvalho Chehab */
430c0d06caSMauro Carvalho Chehab
uvc_find_endpoint(struct usb_host_interface * alts,u8 epaddr)440c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
452c6b222cSLaurent Pinchart u8 epaddr)
460c0d06caSMauro Carvalho Chehab {
470c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep;
480c0d06caSMauro Carvalho Chehab unsigned int i;
490c0d06caSMauro Carvalho Chehab
500c0d06caSMauro Carvalho Chehab for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
510c0d06caSMauro Carvalho Chehab ep = &alts->endpoint[i];
520c0d06caSMauro Carvalho Chehab if (ep->desc.bEndpointAddress == epaddr)
530c0d06caSMauro Carvalho Chehab return ep;
540c0d06caSMauro Carvalho Chehab }
550c0d06caSMauro Carvalho Chehab
560c0d06caSMauro Carvalho Chehab return NULL;
570c0d06caSMauro Carvalho Chehab }
580c0d06caSMauro Carvalho Chehab
uvc_colorspace(const u8 primaries)59ec2c23f6SAdam Goode static enum v4l2_colorspace uvc_colorspace(const u8 primaries)
600c0d06caSMauro Carvalho Chehab {
61ec2c23f6SAdam Goode static const enum v4l2_colorspace colorprimaries[] = {
62e82822faSRicardo Ribalda V4L2_COLORSPACE_SRGB, /* Unspecified */
630c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SRGB,
640c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_470_SYSTEM_M,
650c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_470_SYSTEM_BG,
660c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SMPTE170M,
670c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SMPTE240M,
680c0d06caSMauro Carvalho Chehab };
690c0d06caSMauro Carvalho Chehab
700c0d06caSMauro Carvalho Chehab if (primaries < ARRAY_SIZE(colorprimaries))
710c0d06caSMauro Carvalho Chehab return colorprimaries[primaries];
720c0d06caSMauro Carvalho Chehab
73e82822faSRicardo Ribalda return V4L2_COLORSPACE_SRGB; /* Reserved */
74ec2c23f6SAdam Goode }
75ec2c23f6SAdam Goode
uvc_xfer_func(const u8 transfer_characteristics)76ec2c23f6SAdam Goode static enum v4l2_xfer_func uvc_xfer_func(const u8 transfer_characteristics)
77ec2c23f6SAdam Goode {
78ec2c23f6SAdam Goode /*
79ec2c23f6SAdam Goode * V4L2 does not currently have definitions for all possible values of
80ec2c23f6SAdam Goode * UVC transfer characteristics. If v4l2_xfer_func is extended with new
81ec2c23f6SAdam Goode * values, the mapping below should be updated.
82ec2c23f6SAdam Goode *
83ec2c23f6SAdam Goode * Substitutions are taken from the mapping given for
84ec2c23f6SAdam Goode * V4L2_XFER_FUNC_DEFAULT documented in videodev2.h.
85ec2c23f6SAdam Goode */
86ec2c23f6SAdam Goode static const enum v4l2_xfer_func xfer_funcs[] = {
87ec2c23f6SAdam Goode V4L2_XFER_FUNC_DEFAULT, /* Unspecified */
88ec2c23f6SAdam Goode V4L2_XFER_FUNC_709,
89ec2c23f6SAdam Goode V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 M */
90ec2c23f6SAdam Goode V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 B, G */
91ec2c23f6SAdam Goode V4L2_XFER_FUNC_709, /* Substitution for SMPTE 170M */
92ec2c23f6SAdam Goode V4L2_XFER_FUNC_SMPTE240M,
93ec2c23f6SAdam Goode V4L2_XFER_FUNC_NONE,
94ec2c23f6SAdam Goode V4L2_XFER_FUNC_SRGB,
95ec2c23f6SAdam Goode };
96ec2c23f6SAdam Goode
97ec2c23f6SAdam Goode if (transfer_characteristics < ARRAY_SIZE(xfer_funcs))
98ec2c23f6SAdam Goode return xfer_funcs[transfer_characteristics];
99ec2c23f6SAdam Goode
100ec2c23f6SAdam Goode return V4L2_XFER_FUNC_DEFAULT; /* Reserved */
101ec2c23f6SAdam Goode }
102ec2c23f6SAdam Goode
uvc_ycbcr_enc(const u8 matrix_coefficients)103ec2c23f6SAdam Goode static enum v4l2_ycbcr_encoding uvc_ycbcr_enc(const u8 matrix_coefficients)
104ec2c23f6SAdam Goode {
105ec2c23f6SAdam Goode /*
106ec2c23f6SAdam Goode * V4L2 does not currently have definitions for all possible values of
107ec2c23f6SAdam Goode * UVC matrix coefficients. If v4l2_ycbcr_encoding is extended with new
108ec2c23f6SAdam Goode * values, the mapping below should be updated.
109ec2c23f6SAdam Goode *
110ec2c23f6SAdam Goode * Substitutions are taken from the mapping given for
111ec2c23f6SAdam Goode * V4L2_YCBCR_ENC_DEFAULT documented in videodev2.h.
112ec2c23f6SAdam Goode *
113ec2c23f6SAdam Goode * FCC is assumed to be close enough to 601.
114ec2c23f6SAdam Goode */
115ec2c23f6SAdam Goode static const enum v4l2_ycbcr_encoding ycbcr_encs[] = {
116ec2c23f6SAdam Goode V4L2_YCBCR_ENC_DEFAULT, /* Unspecified */
117ec2c23f6SAdam Goode V4L2_YCBCR_ENC_709,
118ec2c23f6SAdam Goode V4L2_YCBCR_ENC_601, /* Substitution for FCC */
119ec2c23f6SAdam Goode V4L2_YCBCR_ENC_601, /* Substitution for BT.470-2 B, G */
120ec2c23f6SAdam Goode V4L2_YCBCR_ENC_601,
121ec2c23f6SAdam Goode V4L2_YCBCR_ENC_SMPTE240M,
122ec2c23f6SAdam Goode };
123ec2c23f6SAdam Goode
124ec2c23f6SAdam Goode if (matrix_coefficients < ARRAY_SIZE(ycbcr_encs))
125ec2c23f6SAdam Goode return ycbcr_encs[matrix_coefficients];
126ec2c23f6SAdam Goode
127ec2c23f6SAdam Goode return V4L2_YCBCR_ENC_DEFAULT; /* Reserved */
1280c0d06caSMauro Carvalho Chehab }
1290c0d06caSMauro Carvalho Chehab
1300c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
1310c0d06caSMauro Carvalho Chehab * Terminal and unit management
1320c0d06caSMauro Carvalho Chehab */
1330c0d06caSMauro Carvalho Chehab
uvc_entity_by_id(struct uvc_device * dev,int id)1340c0d06caSMauro Carvalho Chehab struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id)
1350c0d06caSMauro Carvalho Chehab {
1360c0d06caSMauro Carvalho Chehab struct uvc_entity *entity;
1370c0d06caSMauro Carvalho Chehab
1380c0d06caSMauro Carvalho Chehab list_for_each_entry(entity, &dev->entities, list) {
1390c0d06caSMauro Carvalho Chehab if (entity->id == id)
1400c0d06caSMauro Carvalho Chehab return entity;
1410c0d06caSMauro Carvalho Chehab }
1420c0d06caSMauro Carvalho Chehab
1430c0d06caSMauro Carvalho Chehab return NULL;
1440c0d06caSMauro Carvalho Chehab }
1450c0d06caSMauro Carvalho Chehab
uvc_entity_by_reference(struct uvc_device * dev,int id,struct uvc_entity * entity)1460c0d06caSMauro Carvalho Chehab static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
1470c0d06caSMauro Carvalho Chehab int id, struct uvc_entity *entity)
1480c0d06caSMauro Carvalho Chehab {
1490c0d06caSMauro Carvalho Chehab unsigned int i;
1500c0d06caSMauro Carvalho Chehab
1510c0d06caSMauro Carvalho Chehab if (entity == NULL)
1520c0d06caSMauro Carvalho Chehab entity = list_entry(&dev->entities, struct uvc_entity, list);
1530c0d06caSMauro Carvalho Chehab
1540c0d06caSMauro Carvalho Chehab list_for_each_entry_continue(entity, &dev->entities, list) {
1550c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->bNrInPins; ++i)
1560c0d06caSMauro Carvalho Chehab if (entity->baSourceID[i] == id)
1570c0d06caSMauro Carvalho Chehab return entity;
1580c0d06caSMauro Carvalho Chehab }
1590c0d06caSMauro Carvalho Chehab
1600c0d06caSMauro Carvalho Chehab return NULL;
1610c0d06caSMauro Carvalho Chehab }
1620c0d06caSMauro Carvalho Chehab
uvc_stream_by_id(struct uvc_device * dev,int id)1630c0d06caSMauro Carvalho Chehab static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id)
1640c0d06caSMauro Carvalho Chehab {
1650c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream;
1660c0d06caSMauro Carvalho Chehab
1670c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) {
1680c0d06caSMauro Carvalho Chehab if (stream->header.bTerminalLink == id)
1690c0d06caSMauro Carvalho Chehab return stream;
1700c0d06caSMauro Carvalho Chehab }
1710c0d06caSMauro Carvalho Chehab
1720c0d06caSMauro Carvalho Chehab return NULL;
1730c0d06caSMauro Carvalho Chehab }
1740c0d06caSMauro Carvalho Chehab
1750c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
176ece41454SKieran Bingham * Streaming Object Management
177ece41454SKieran Bingham */
178ece41454SKieran Bingham
uvc_stream_delete(struct uvc_streaming * stream)179ece41454SKieran Bingham static void uvc_stream_delete(struct uvc_streaming *stream)
180ece41454SKieran Bingham {
181b012186aSKieran Bingham if (stream->async_wq)
182b012186aSKieran Bingham destroy_workqueue(stream->async_wq);
183b012186aSKieran Bingham
184ece41454SKieran Bingham mutex_destroy(&stream->mutex);
185ece41454SKieran Bingham
186ece41454SKieran Bingham usb_put_intf(stream->intf);
187ece41454SKieran Bingham
188ccfad4e8SLaurent Pinchart kfree(stream->formats);
189ece41454SKieran Bingham kfree(stream->header.bmaControls);
190ece41454SKieran Bingham kfree(stream);
191ece41454SKieran Bingham }
192ece41454SKieran Bingham
uvc_stream_new(struct uvc_device * dev,struct usb_interface * intf)193ece41454SKieran Bingham static struct uvc_streaming *uvc_stream_new(struct uvc_device *dev,
194ece41454SKieran Bingham struct usb_interface *intf)
195ece41454SKieran Bingham {
196ece41454SKieran Bingham struct uvc_streaming *stream;
197ece41454SKieran Bingham
198ece41454SKieran Bingham stream = kzalloc(sizeof(*stream), GFP_KERNEL);
199ece41454SKieran Bingham if (stream == NULL)
200ece41454SKieran Bingham return NULL;
201ece41454SKieran Bingham
202ece41454SKieran Bingham mutex_init(&stream->mutex);
203ece41454SKieran Bingham
204ece41454SKieran Bingham stream->dev = dev;
205ece41454SKieran Bingham stream->intf = usb_get_intf(intf);
206ece41454SKieran Bingham stream->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
207ece41454SKieran Bingham
208b012186aSKieran Bingham /* Allocate a stream specific work queue for asynchronous tasks. */
209b012186aSKieran Bingham stream->async_wq = alloc_workqueue("uvcvideo", WQ_UNBOUND | WQ_HIGHPRI,
210b012186aSKieran Bingham 0);
211b012186aSKieran Bingham if (!stream->async_wq) {
212b012186aSKieran Bingham uvc_stream_delete(stream);
213b012186aSKieran Bingham return NULL;
214b012186aSKieran Bingham }
215b012186aSKieran Bingham
216ece41454SKieran Bingham return stream;
217ece41454SKieran Bingham }
218ece41454SKieran Bingham
219ece41454SKieran Bingham /* ------------------------------------------------------------------------
2200c0d06caSMauro Carvalho Chehab * Descriptors parsing
2210c0d06caSMauro Carvalho Chehab */
2220c0d06caSMauro Carvalho Chehab
uvc_parse_format(struct uvc_device * dev,struct uvc_streaming * streaming,struct uvc_format * format,struct uvc_frame * frames,u32 ** intervals,const unsigned char * buffer,int buflen)2230c0d06caSMauro Carvalho Chehab static int uvc_parse_format(struct uvc_device *dev,
2240c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming, struct uvc_format *format,
225102df33eSLaurent Pinchart struct uvc_frame *frames, u32 **intervals, const unsigned char *buffer,
226af621ba2SLaurent Pinchart int buflen)
2270c0d06caSMauro Carvalho Chehab {
2280c0d06caSMauro Carvalho Chehab struct usb_interface *intf = streaming->intf;
2290c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = intf->cur_altsetting;
2308ecb17a8SMichael Grzeschik const struct uvc_format_desc *fmtdesc;
2310c0d06caSMauro Carvalho Chehab struct uvc_frame *frame;
2320c0d06caSMauro Carvalho Chehab const unsigned char *start = buffer;
233e1b78a33SPhilipp Zabel unsigned int width_multiplier = 1;
2340c0d06caSMauro Carvalho Chehab unsigned int interval;
2350c0d06caSMauro Carvalho Chehab unsigned int i, n;
2362c6b222cSLaurent Pinchart u8 ftype;
2370c0d06caSMauro Carvalho Chehab
2380c0d06caSMauro Carvalho Chehab format->type = buffer[2];
2390c0d06caSMauro Carvalho Chehab format->index = buffer[3];
240af621ba2SLaurent Pinchart format->frames = frames;
2410c0d06caSMauro Carvalho Chehab
2420c0d06caSMauro Carvalho Chehab switch (buffer[2]) {
2430c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED:
2440c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED:
2450c0d06caSMauro Carvalho Chehab n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28;
2460c0d06caSMauro Carvalho Chehab if (buflen < n) {
2479e56380aSJoe Perches uvc_dbg(dev, DESCR,
248ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d FORMAT error\n",
2490c0d06caSMauro Carvalho Chehab dev->udev->devnum,
2500c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber);
2510c0d06caSMauro Carvalho Chehab return -EINVAL;
2520c0d06caSMauro Carvalho Chehab }
2530c0d06caSMauro Carvalho Chehab
2540c0d06caSMauro Carvalho Chehab /* Find the format descriptor from its GUID. */
2550c0d06caSMauro Carvalho Chehab fmtdesc = uvc_format_by_guid(&buffer[5]);
2560c0d06caSMauro Carvalho Chehab
25781f3affaSLaurent Pinchart if (!fmtdesc) {
25881f3affaSLaurent Pinchart /*
25981f3affaSLaurent Pinchart * Unknown video formats are not fatal errors, the
26081f3affaSLaurent Pinchart * caller will skip this descriptor.
26181f3affaSLaurent Pinchart */
26269df0954SRicardo Ribalda dev_info(&streaming->intf->dev,
26369df0954SRicardo Ribalda "Unknown video format %pUl\n", &buffer[5]);
26481f3affaSLaurent Pinchart return 0;
2650c0d06caSMauro Carvalho Chehab }
2660c0d06caSMauro Carvalho Chehab
26781f3affaSLaurent Pinchart format->fcc = fmtdesc->fcc;
2680c0d06caSMauro Carvalho Chehab format->bpp = buffer[21];
269e1b78a33SPhilipp Zabel
270699b9a86SLaurent Pinchart /*
271699b9a86SLaurent Pinchart * Some devices report a format that doesn't match what they
272e1b78a33SPhilipp Zabel * really send.
273e1b78a33SPhilipp Zabel */
274e1b78a33SPhilipp Zabel if (dev->quirks & UVC_QUIRK_FORCE_Y8) {
275e1b78a33SPhilipp Zabel if (format->fcc == V4L2_PIX_FMT_YUYV) {
276e1b78a33SPhilipp Zabel format->fcc = V4L2_PIX_FMT_GREY;
277e1b78a33SPhilipp Zabel format->bpp = 8;
278e1b78a33SPhilipp Zabel width_multiplier = 2;
279e1b78a33SPhilipp Zabel }
280e1b78a33SPhilipp Zabel }
281e1b78a33SPhilipp Zabel
2821dd2e8f9SSergey Zakharchenko /* Some devices report bpp that doesn't match the format. */
2831dd2e8f9SSergey Zakharchenko if (dev->quirks & UVC_QUIRK_FORCE_BPP) {
2841dd2e8f9SSergey Zakharchenko const struct v4l2_format_info *info =
2851dd2e8f9SSergey Zakharchenko v4l2_format_info(format->fcc);
2861dd2e8f9SSergey Zakharchenko
2871dd2e8f9SSergey Zakharchenko if (info) {
2881dd2e8f9SSergey Zakharchenko unsigned int div = info->hdiv * info->vdiv;
2891dd2e8f9SSergey Zakharchenko
2901dd2e8f9SSergey Zakharchenko n = info->bpp[0] * div;
2911dd2e8f9SSergey Zakharchenko for (i = 1; i < info->comp_planes; i++)
2921dd2e8f9SSergey Zakharchenko n += info->bpp[i];
2931dd2e8f9SSergey Zakharchenko
2941dd2e8f9SSergey Zakharchenko format->bpp = DIV_ROUND_UP(8 * n, div);
2951dd2e8f9SSergey Zakharchenko }
2961dd2e8f9SSergey Zakharchenko }
2971dd2e8f9SSergey Zakharchenko
2980c0d06caSMauro Carvalho Chehab if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) {
2990c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_UNCOMPRESSED;
3000c0d06caSMauro Carvalho Chehab } else {
3010c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_FRAME_BASED;
3020c0d06caSMauro Carvalho Chehab if (buffer[27])
3030c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED;
3040c0d06caSMauro Carvalho Chehab }
3050c0d06caSMauro Carvalho Chehab break;
3060c0d06caSMauro Carvalho Chehab
3070c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG:
3080c0d06caSMauro Carvalho Chehab if (buflen < 11) {
3099e56380aSJoe Perches uvc_dbg(dev, DESCR,
310ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d FORMAT error\n",
3110c0d06caSMauro Carvalho Chehab dev->udev->devnum,
3120c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber);
3130c0d06caSMauro Carvalho Chehab return -EINVAL;
3140c0d06caSMauro Carvalho Chehab }
3150c0d06caSMauro Carvalho Chehab
3160c0d06caSMauro Carvalho Chehab format->fcc = V4L2_PIX_FMT_MJPEG;
3170c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED;
3180c0d06caSMauro Carvalho Chehab format->bpp = 0;
3190c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_MJPEG;
3200c0d06caSMauro Carvalho Chehab break;
3210c0d06caSMauro Carvalho Chehab
3220c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV:
3230c0d06caSMauro Carvalho Chehab if (buflen < 9) {
3249e56380aSJoe Perches uvc_dbg(dev, DESCR,
325ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d FORMAT error\n",
3260c0d06caSMauro Carvalho Chehab dev->udev->devnum,
3270c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber);
3280c0d06caSMauro Carvalho Chehab return -EINVAL;
3290c0d06caSMauro Carvalho Chehab }
3300c0d06caSMauro Carvalho Chehab
33150459f10SLaurent Pinchart if ((buffer[8] & 0x7f) > 2) {
3329e56380aSJoe Perches uvc_dbg(dev, DESCR,
333ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d: unknown DV format %u\n",
3340c0d06caSMauro Carvalho Chehab dev->udev->devnum,
3350c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, buffer[8]);
3360c0d06caSMauro Carvalho Chehab return -EINVAL;
3370c0d06caSMauro Carvalho Chehab }
3380c0d06caSMauro Carvalho Chehab
3390c0d06caSMauro Carvalho Chehab format->fcc = V4L2_PIX_FMT_DV;
3400c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM;
3410c0d06caSMauro Carvalho Chehab format->bpp = 0;
3420c0d06caSMauro Carvalho Chehab ftype = 0;
3430c0d06caSMauro Carvalho Chehab
3440c0d06caSMauro Carvalho Chehab /* Create a dummy frame descriptor. */
345af621ba2SLaurent Pinchart frame = &frames[0];
346af621ba2SLaurent Pinchart memset(frame, 0, sizeof(*frame));
3470c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = 1;
3480c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 1;
3490c0d06caSMauro Carvalho Chehab frame->dwFrameInterval = *intervals;
3500c0d06caSMauro Carvalho Chehab *(*intervals)++ = 1;
3510c0d06caSMauro Carvalho Chehab format->nframes = 1;
3520c0d06caSMauro Carvalho Chehab break;
3530c0d06caSMauro Carvalho Chehab
3540c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MPEG2TS:
3550c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_STREAM_BASED:
3560c0d06caSMauro Carvalho Chehab /* Not supported yet. */
3570c0d06caSMauro Carvalho Chehab default:
3589e56380aSJoe Perches uvc_dbg(dev, DESCR,
359ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d unsupported format %u\n",
3600c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber,
3610c0d06caSMauro Carvalho Chehab buffer[2]);
3620c0d06caSMauro Carvalho Chehab return -EINVAL;
3630c0d06caSMauro Carvalho Chehab }
3640c0d06caSMauro Carvalho Chehab
36550459f10SLaurent Pinchart uvc_dbg(dev, DESCR, "Found format %p4cc", &format->fcc);
3660c0d06caSMauro Carvalho Chehab
3670c0d06caSMauro Carvalho Chehab buflen -= buffer[0];
3680c0d06caSMauro Carvalho Chehab buffer += buffer[0];
3690c0d06caSMauro Carvalho Chehab
370699b9a86SLaurent Pinchart /*
371699b9a86SLaurent Pinchart * Parse the frame descriptors. Only uncompressed, MJPEG and frame
3720c0d06caSMauro Carvalho Chehab * based formats have frame descriptors.
3730c0d06caSMauro Carvalho Chehab */
374575a562fSBenoit Sevens while (ftype && buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
3750c0d06caSMauro Carvalho Chehab buffer[2] == ftype) {
3767691d900SLaurent Pinchart unsigned int maxIntervalIndex;
3777691d900SLaurent Pinchart
378af621ba2SLaurent Pinchart frame = &frames[format->nframes];
3790c0d06caSMauro Carvalho Chehab if (ftype != UVC_VS_FRAME_FRAME_BASED)
3800c0d06caSMauro Carvalho Chehab n = buflen > 25 ? buffer[25] : 0;
3810c0d06caSMauro Carvalho Chehab else
3820c0d06caSMauro Carvalho Chehab n = buflen > 21 ? buffer[21] : 0;
3830c0d06caSMauro Carvalho Chehab
3840c0d06caSMauro Carvalho Chehab n = n ? n : 3;
3850c0d06caSMauro Carvalho Chehab
3860c0d06caSMauro Carvalho Chehab if (buflen < 26 + 4*n) {
3879e56380aSJoe Perches uvc_dbg(dev, DESCR,
388ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d FRAME error\n",
389ed4c5fa4SRicardo Ribalda dev->udev->devnum,
3900c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber);
3910c0d06caSMauro Carvalho Chehab return -EINVAL;
3920c0d06caSMauro Carvalho Chehab }
3930c0d06caSMauro Carvalho Chehab
3940c0d06caSMauro Carvalho Chehab frame->bFrameIndex = buffer[3];
3950c0d06caSMauro Carvalho Chehab frame->bmCapabilities = buffer[4];
396e1b78a33SPhilipp Zabel frame->wWidth = get_unaligned_le16(&buffer[5])
397e1b78a33SPhilipp Zabel * width_multiplier;
3980c0d06caSMauro Carvalho Chehab frame->wHeight = get_unaligned_le16(&buffer[7]);
3990c0d06caSMauro Carvalho Chehab frame->dwMinBitRate = get_unaligned_le32(&buffer[9]);
4000c0d06caSMauro Carvalho Chehab frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]);
4010c0d06caSMauro Carvalho Chehab if (ftype != UVC_VS_FRAME_FRAME_BASED) {
4020c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize =
4030c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[17]);
4040c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval =
4050c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[21]);
4060c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = buffer[25];
4070c0d06caSMauro Carvalho Chehab } else {
4080c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = 0;
4090c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval =
4100c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[17]);
4110c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = buffer[21];
4120c0d06caSMauro Carvalho Chehab }
413c9d597b9SLaurent Pinchart
414c9d597b9SLaurent Pinchart /*
415c9d597b9SLaurent Pinchart * Copy the frame intervals.
416c9d597b9SLaurent Pinchart *
417c9d597b9SLaurent Pinchart * Some bogus devices report dwMinFrameInterval equal to
418c9d597b9SLaurent Pinchart * dwMaxFrameInterval and have dwFrameIntervalStep set to
419c9d597b9SLaurent Pinchart * zero. Setting all null intervals to 1 fixes the problem and
420c9d597b9SLaurent Pinchart * some other divisions by zero that could happen.
421c9d597b9SLaurent Pinchart */
4220c0d06caSMauro Carvalho Chehab frame->dwFrameInterval = *intervals;
4230c0d06caSMauro Carvalho Chehab
424c9d597b9SLaurent Pinchart for (i = 0; i < n; ++i) {
425c9d597b9SLaurent Pinchart interval = get_unaligned_le32(&buffer[26+4*i]);
4267691d900SLaurent Pinchart (*intervals)[i] = interval ? interval : 1;
427c9d597b9SLaurent Pinchart }
428c9d597b9SLaurent Pinchart
429c9d597b9SLaurent Pinchart /*
430c9d597b9SLaurent Pinchart * Apply more fixes, quirks and workarounds to handle incorrect
431c9d597b9SLaurent Pinchart * or broken descriptors.
432c9d597b9SLaurent Pinchart */
433c9d597b9SLaurent Pinchart
434699b9a86SLaurent Pinchart /*
435699b9a86SLaurent Pinchart * Several UVC chipsets screw up dwMaxVideoFrameBufferSize
4360c0d06caSMauro Carvalho Chehab * completely. Observed behaviours range from setting the
4370c0d06caSMauro Carvalho Chehab * value to 1.1x the actual frame size to hardwiring the
4380c0d06caSMauro Carvalho Chehab * 16 low bits to 0. This results in a higher than necessary
4390c0d06caSMauro Carvalho Chehab * memory usage as well as a wrong image size information. For
4400c0d06caSMauro Carvalho Chehab * uncompressed formats this can be fixed by computing the
4410c0d06caSMauro Carvalho Chehab * value from the frame size.
4420c0d06caSMauro Carvalho Chehab */
4430c0d06caSMauro Carvalho Chehab if (!(format->flags & UVC_FMT_FLAG_COMPRESSED))
4440c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = format->bpp
4450c0d06caSMauro Carvalho Chehab * frame->wWidth * frame->wHeight / 8;
4460c0d06caSMauro Carvalho Chehab
4477691d900SLaurent Pinchart /*
4487691d900SLaurent Pinchart * Clamp the default frame interval to the boundaries. A zero
4497691d900SLaurent Pinchart * bFrameIntervalType value indicates a continuous frame
4507691d900SLaurent Pinchart * interval range, with dwFrameInterval[0] storing the minimum
4517691d900SLaurent Pinchart * value and dwFrameInterval[1] storing the maximum value.
4527691d900SLaurent Pinchart */
4537691d900SLaurent Pinchart maxIntervalIndex = frame->bFrameIntervalType ? n - 1 : 1;
4540c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval =
4557c5dfb2dSLaurent Pinchart clamp(frame->dwDefaultFrameInterval,
4567c5dfb2dSLaurent Pinchart frame->dwFrameInterval[0],
4577691d900SLaurent Pinchart frame->dwFrameInterval[maxIntervalIndex]);
4580c0d06caSMauro Carvalho Chehab
459c9d597b9SLaurent Pinchart /*
460c9d597b9SLaurent Pinchart * Some devices report frame intervals that are not functional.
461c9d597b9SLaurent Pinchart * If the corresponding quirk is set, restrict operation to the
462c9d597b9SLaurent Pinchart * first interval only.
463c9d597b9SLaurent Pinchart */
4640c0d06caSMauro Carvalho Chehab if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) {
4650c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = 1;
466af621ba2SLaurent Pinchart (*intervals)[0] = frame->dwDefaultFrameInterval;
4670c0d06caSMauro Carvalho Chehab }
4680c0d06caSMauro Carvalho Chehab
4699e56380aSJoe Perches uvc_dbg(dev, DESCR, "- %ux%u (%u.%u fps)\n",
4700c0d06caSMauro Carvalho Chehab frame->wWidth, frame->wHeight,
4710c0d06caSMauro Carvalho Chehab 10000000 / frame->dwDefaultFrameInterval,
4720c0d06caSMauro Carvalho Chehab (100000000 / frame->dwDefaultFrameInterval) % 10);
4730c0d06caSMauro Carvalho Chehab
4740c0d06caSMauro Carvalho Chehab format->nframes++;
4757691d900SLaurent Pinchart *intervals += n;
4767691d900SLaurent Pinchart
4770c0d06caSMauro Carvalho Chehab buflen -= buffer[0];
4780c0d06caSMauro Carvalho Chehab buffer += buffer[0];
4790c0d06caSMauro Carvalho Chehab }
4800c0d06caSMauro Carvalho Chehab
4810c0d06caSMauro Carvalho Chehab if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
4820c0d06caSMauro Carvalho Chehab buffer[2] == UVC_VS_STILL_IMAGE_FRAME) {
4830c0d06caSMauro Carvalho Chehab buflen -= buffer[0];
4840c0d06caSMauro Carvalho Chehab buffer += buffer[0];
4850c0d06caSMauro Carvalho Chehab }
4860c0d06caSMauro Carvalho Chehab
4870c0d06caSMauro Carvalho Chehab if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
4880c0d06caSMauro Carvalho Chehab buffer[2] == UVC_VS_COLORFORMAT) {
4890c0d06caSMauro Carvalho Chehab if (buflen < 6) {
4909e56380aSJoe Perches uvc_dbg(dev, DESCR,
491ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d COLORFORMAT error\n",
4920c0d06caSMauro Carvalho Chehab dev->udev->devnum,
4930c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber);
4940c0d06caSMauro Carvalho Chehab return -EINVAL;
4950c0d06caSMauro Carvalho Chehab }
4960c0d06caSMauro Carvalho Chehab
4970c0d06caSMauro Carvalho Chehab format->colorspace = uvc_colorspace(buffer[3]);
498ec2c23f6SAdam Goode format->xfer_func = uvc_xfer_func(buffer[4]);
499ec2c23f6SAdam Goode format->ycbcr_enc = uvc_ycbcr_enc(buffer[5]);
5000c0d06caSMauro Carvalho Chehab
5010c0d06caSMauro Carvalho Chehab buflen -= buffer[0];
5020c0d06caSMauro Carvalho Chehab buffer += buffer[0];
503e82822faSRicardo Ribalda } else {
504e82822faSRicardo Ribalda format->colorspace = V4L2_COLORSPACE_SRGB;
5050c0d06caSMauro Carvalho Chehab }
5060c0d06caSMauro Carvalho Chehab
5070c0d06caSMauro Carvalho Chehab return buffer - start;
5080c0d06caSMauro Carvalho Chehab }
5090c0d06caSMauro Carvalho Chehab
uvc_parse_streaming(struct uvc_device * dev,struct usb_interface * intf)5100c0d06caSMauro Carvalho Chehab static int uvc_parse_streaming(struct uvc_device *dev,
5110c0d06caSMauro Carvalho Chehab struct usb_interface *intf)
5120c0d06caSMauro Carvalho Chehab {
5130c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming = NULL;
5140c0d06caSMauro Carvalho Chehab struct uvc_format *format;
5150c0d06caSMauro Carvalho Chehab struct uvc_frame *frame;
5160c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = &intf->altsetting[0];
517102df33eSLaurent Pinchart const unsigned char *_buffer, *buffer = alts->extra;
5180c0d06caSMauro Carvalho Chehab int _buflen, buflen = alts->extralen;
5190c0d06caSMauro Carvalho Chehab unsigned int nformats = 0, nframes = 0, nintervals = 0;
5200c0d06caSMauro Carvalho Chehab unsigned int size, i, n, p;
5212c6b222cSLaurent Pinchart u32 *interval;
5222c6b222cSLaurent Pinchart u16 psize;
5230c0d06caSMauro Carvalho Chehab int ret = -EINVAL;
5240c0d06caSMauro Carvalho Chehab
5250c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass
5260c0d06caSMauro Carvalho Chehab != UVC_SC_VIDEOSTREAMING) {
5279e56380aSJoe Perches uvc_dbg(dev, DESCR,
528ed4c5fa4SRicardo Ribalda "device %d interface %d isn't a video streaming interface\n",
529ed4c5fa4SRicardo Ribalda dev->udev->devnum,
5300c0d06caSMauro Carvalho Chehab intf->altsetting[0].desc.bInterfaceNumber);
5310c0d06caSMauro Carvalho Chehab return -EINVAL;
5320c0d06caSMauro Carvalho Chehab }
5330c0d06caSMauro Carvalho Chehab
5340c0d06caSMauro Carvalho Chehab if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) {
5359e56380aSJoe Perches uvc_dbg(dev, DESCR,
536ed4c5fa4SRicardo Ribalda "device %d interface %d is already claimed\n",
537ed4c5fa4SRicardo Ribalda dev->udev->devnum,
5380c0d06caSMauro Carvalho Chehab intf->altsetting[0].desc.bInterfaceNumber);
5390c0d06caSMauro Carvalho Chehab return -EINVAL;
5400c0d06caSMauro Carvalho Chehab }
5410c0d06caSMauro Carvalho Chehab
542ece41454SKieran Bingham streaming = uvc_stream_new(dev, intf);
5430c0d06caSMauro Carvalho Chehab if (streaming == NULL) {
5440c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, intf);
545ece41454SKieran Bingham return -ENOMEM;
5460c0d06caSMauro Carvalho Chehab }
5470c0d06caSMauro Carvalho Chehab
548699b9a86SLaurent Pinchart /*
549699b9a86SLaurent Pinchart * The Pico iMage webcam has its class-specific interface descriptors
5500c0d06caSMauro Carvalho Chehab * after the endpoint descriptors.
5510c0d06caSMauro Carvalho Chehab */
5520c0d06caSMauro Carvalho Chehab if (buflen == 0) {
5530c0d06caSMauro Carvalho Chehab for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
5540c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep = &alts->endpoint[i];
5550c0d06caSMauro Carvalho Chehab
5560c0d06caSMauro Carvalho Chehab if (ep->extralen == 0)
5570c0d06caSMauro Carvalho Chehab continue;
5580c0d06caSMauro Carvalho Chehab
5590c0d06caSMauro Carvalho Chehab if (ep->extralen > 2 &&
5600c0d06caSMauro Carvalho Chehab ep->extra[1] == USB_DT_CS_INTERFACE) {
5619e56380aSJoe Perches uvc_dbg(dev, DESCR,
5629e56380aSJoe Perches "trying extra data from endpoint %u\n",
563ed4c5fa4SRicardo Ribalda i);
5640c0d06caSMauro Carvalho Chehab buffer = alts->endpoint[i].extra;
5650c0d06caSMauro Carvalho Chehab buflen = alts->endpoint[i].extralen;
5660c0d06caSMauro Carvalho Chehab break;
5670c0d06caSMauro Carvalho Chehab }
5680c0d06caSMauro Carvalho Chehab }
5690c0d06caSMauro Carvalho Chehab }
5700c0d06caSMauro Carvalho Chehab
5710c0d06caSMauro Carvalho Chehab /* Skip the standard interface descriptors. */
5720c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) {
5730c0d06caSMauro Carvalho Chehab buflen -= buffer[0];
5740c0d06caSMauro Carvalho Chehab buffer += buffer[0];
5750c0d06caSMauro Carvalho Chehab }
5760c0d06caSMauro Carvalho Chehab
5770c0d06caSMauro Carvalho Chehab if (buflen <= 2) {
5789e56380aSJoe Perches uvc_dbg(dev, DESCR,
5799e56380aSJoe Perches "no class-specific streaming interface descriptors found\n");
5800c0d06caSMauro Carvalho Chehab goto error;
5810c0d06caSMauro Carvalho Chehab }
5820c0d06caSMauro Carvalho Chehab
5830c0d06caSMauro Carvalho Chehab /* Parse the header descriptor. */
5840c0d06caSMauro Carvalho Chehab switch (buffer[2]) {
5850c0d06caSMauro Carvalho Chehab case UVC_VS_OUTPUT_HEADER:
5860c0d06caSMauro Carvalho Chehab streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
5870c0d06caSMauro Carvalho Chehab size = 9;
5880c0d06caSMauro Carvalho Chehab break;
5890c0d06caSMauro Carvalho Chehab
5900c0d06caSMauro Carvalho Chehab case UVC_VS_INPUT_HEADER:
5910c0d06caSMauro Carvalho Chehab streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
5920c0d06caSMauro Carvalho Chehab size = 13;
5930c0d06caSMauro Carvalho Chehab break;
5940c0d06caSMauro Carvalho Chehab
5950c0d06caSMauro Carvalho Chehab default:
5969e56380aSJoe Perches uvc_dbg(dev, DESCR,
5979e56380aSJoe Perches "device %d videostreaming interface %d HEADER descriptor not found\n",
598ed4c5fa4SRicardo Ribalda dev->udev->devnum, alts->desc.bInterfaceNumber);
5990c0d06caSMauro Carvalho Chehab goto error;
6000c0d06caSMauro Carvalho Chehab }
6010c0d06caSMauro Carvalho Chehab
6020c0d06caSMauro Carvalho Chehab p = buflen >= 4 ? buffer[3] : 0;
6030c0d06caSMauro Carvalho Chehab n = buflen >= size ? buffer[size-1] : 0;
6040c0d06caSMauro Carvalho Chehab
6050c0d06caSMauro Carvalho Chehab if (buflen < size + p*n) {
6069e56380aSJoe Perches uvc_dbg(dev, DESCR,
6079e56380aSJoe Perches "device %d videostreaming interface %d HEADER descriptor is invalid\n",
6080c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber);
6090c0d06caSMauro Carvalho Chehab goto error;
6100c0d06caSMauro Carvalho Chehab }
6110c0d06caSMauro Carvalho Chehab
6120c0d06caSMauro Carvalho Chehab streaming->header.bNumFormats = p;
6130c0d06caSMauro Carvalho Chehab streaming->header.bEndpointAddress = buffer[6];
6140c0d06caSMauro Carvalho Chehab if (buffer[2] == UVC_VS_INPUT_HEADER) {
6150c0d06caSMauro Carvalho Chehab streaming->header.bmInfo = buffer[7];
6160c0d06caSMauro Carvalho Chehab streaming->header.bTerminalLink = buffer[8];
6170c0d06caSMauro Carvalho Chehab streaming->header.bStillCaptureMethod = buffer[9];
6180c0d06caSMauro Carvalho Chehab streaming->header.bTriggerSupport = buffer[10];
6190c0d06caSMauro Carvalho Chehab streaming->header.bTriggerUsage = buffer[11];
6200c0d06caSMauro Carvalho Chehab } else {
6210c0d06caSMauro Carvalho Chehab streaming->header.bTerminalLink = buffer[7];
6220c0d06caSMauro Carvalho Chehab }
6230c0d06caSMauro Carvalho Chehab streaming->header.bControlSize = n;
6240c0d06caSMauro Carvalho Chehab
6250c0d06caSMauro Carvalho Chehab streaming->header.bmaControls = kmemdup(&buffer[size], p * n,
6260c0d06caSMauro Carvalho Chehab GFP_KERNEL);
6270c0d06caSMauro Carvalho Chehab if (streaming->header.bmaControls == NULL) {
6280c0d06caSMauro Carvalho Chehab ret = -ENOMEM;
6290c0d06caSMauro Carvalho Chehab goto error;
6300c0d06caSMauro Carvalho Chehab }
6310c0d06caSMauro Carvalho Chehab
6320c0d06caSMauro Carvalho Chehab buflen -= buffer[0];
6330c0d06caSMauro Carvalho Chehab buffer += buffer[0];
6340c0d06caSMauro Carvalho Chehab
6350c0d06caSMauro Carvalho Chehab _buffer = buffer;
6360c0d06caSMauro Carvalho Chehab _buflen = buflen;
6370c0d06caSMauro Carvalho Chehab
6380c0d06caSMauro Carvalho Chehab /* Count the format and frame descriptors. */
6390c0d06caSMauro Carvalho Chehab while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) {
6400c0d06caSMauro Carvalho Chehab switch (_buffer[2]) {
6410c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED:
6420c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG:
6430c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED:
6440c0d06caSMauro Carvalho Chehab nformats++;
6450c0d06caSMauro Carvalho Chehab break;
6460c0d06caSMauro Carvalho Chehab
6470c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV:
648699b9a86SLaurent Pinchart /*
649699b9a86SLaurent Pinchart * DV format has no frame descriptor. We will create a
6500c0d06caSMauro Carvalho Chehab * dummy frame descriptor with a dummy frame interval.
6510c0d06caSMauro Carvalho Chehab */
6520c0d06caSMauro Carvalho Chehab nformats++;
6530c0d06caSMauro Carvalho Chehab nframes++;
6540c0d06caSMauro Carvalho Chehab nintervals++;
6550c0d06caSMauro Carvalho Chehab break;
6560c0d06caSMauro Carvalho Chehab
6570c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MPEG2TS:
6580c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_STREAM_BASED:
6599e56380aSJoe Perches uvc_dbg(dev, DESCR,
6609e56380aSJoe Perches "device %d videostreaming interface %d FORMAT %u is not supported\n",
6610c0d06caSMauro Carvalho Chehab dev->udev->devnum,
6620c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, _buffer[2]);
6630c0d06caSMauro Carvalho Chehab break;
6640c0d06caSMauro Carvalho Chehab
6650c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_UNCOMPRESSED:
6660c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_MJPEG:
6670c0d06caSMauro Carvalho Chehab nframes++;
6680c0d06caSMauro Carvalho Chehab if (_buflen > 25)
6690c0d06caSMauro Carvalho Chehab nintervals += _buffer[25] ? _buffer[25] : 3;
6700c0d06caSMauro Carvalho Chehab break;
6710c0d06caSMauro Carvalho Chehab
6720c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_FRAME_BASED:
6730c0d06caSMauro Carvalho Chehab nframes++;
6740c0d06caSMauro Carvalho Chehab if (_buflen > 21)
6750c0d06caSMauro Carvalho Chehab nintervals += _buffer[21] ? _buffer[21] : 3;
6760c0d06caSMauro Carvalho Chehab break;
6770c0d06caSMauro Carvalho Chehab }
6780c0d06caSMauro Carvalho Chehab
6790c0d06caSMauro Carvalho Chehab _buflen -= _buffer[0];
6800c0d06caSMauro Carvalho Chehab _buffer += _buffer[0];
6810c0d06caSMauro Carvalho Chehab }
6820c0d06caSMauro Carvalho Chehab
6830c0d06caSMauro Carvalho Chehab if (nformats == 0) {
6849e56380aSJoe Perches uvc_dbg(dev, DESCR,
6859e56380aSJoe Perches "device %d videostreaming interface %d has no supported formats defined\n",
6860c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber);
6870c0d06caSMauro Carvalho Chehab goto error;
6880c0d06caSMauro Carvalho Chehab }
6890c0d06caSMauro Carvalho Chehab
690d8915d27SRicardo Ribalda /*
691d8915d27SRicardo Ribalda * Allocate memory for the formats, the frames and the intervals,
692d8915d27SRicardo Ribalda * plus any required padding to guarantee that everything has the
693d8915d27SRicardo Ribalda * correct alignment.
694d8915d27SRicardo Ribalda */
695d8915d27SRicardo Ribalda size = nformats * sizeof(*format);
696d8915d27SRicardo Ribalda size = ALIGN(size, __alignof__(*frame)) + nframes * sizeof(*frame);
697d8915d27SRicardo Ribalda size = ALIGN(size, __alignof__(*interval))
698f14d4988SLaurent Pinchart + nintervals * sizeof(*interval);
699d8915d27SRicardo Ribalda
7000c0d06caSMauro Carvalho Chehab format = kzalloc(size, GFP_KERNEL);
701d8915d27SRicardo Ribalda if (!format) {
7020c0d06caSMauro Carvalho Chehab ret = -ENOMEM;
7030c0d06caSMauro Carvalho Chehab goto error;
7040c0d06caSMauro Carvalho Chehab }
7050c0d06caSMauro Carvalho Chehab
706d8915d27SRicardo Ribalda frame = (void *)format + nformats * sizeof(*format);
707d8915d27SRicardo Ribalda frame = PTR_ALIGN(frame, __alignof__(*frame));
708d8915d27SRicardo Ribalda interval = (void *)frame + nframes * sizeof(*frame);
709d8915d27SRicardo Ribalda interval = PTR_ALIGN(interval, __alignof__(*interval));
7100c0d06caSMauro Carvalho Chehab
711ccfad4e8SLaurent Pinchart streaming->formats = format;
71281f3affaSLaurent Pinchart streaming->nformats = 0;
7130c0d06caSMauro Carvalho Chehab
7140c0d06caSMauro Carvalho Chehab /* Parse the format descriptors. */
7150c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) {
7160c0d06caSMauro Carvalho Chehab switch (buffer[2]) {
7170c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED:
7180c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG:
7190c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV:
7200c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED:
721af621ba2SLaurent Pinchart ret = uvc_parse_format(dev, streaming, format, frame,
7220c0d06caSMauro Carvalho Chehab &interval, buffer, buflen);
7230c0d06caSMauro Carvalho Chehab if (ret < 0)
7240c0d06caSMauro Carvalho Chehab goto error;
72581f3affaSLaurent Pinchart if (!ret)
72681f3affaSLaurent Pinchart break;
7270c0d06caSMauro Carvalho Chehab
72881f3affaSLaurent Pinchart streaming->nformats++;
7290c0d06caSMauro Carvalho Chehab frame += format->nframes;
7300c0d06caSMauro Carvalho Chehab format++;
7310c0d06caSMauro Carvalho Chehab
7320c0d06caSMauro Carvalho Chehab buflen -= ret;
7330c0d06caSMauro Carvalho Chehab buffer += ret;
7340c0d06caSMauro Carvalho Chehab continue;
7350c0d06caSMauro Carvalho Chehab
7360c0d06caSMauro Carvalho Chehab default:
7370c0d06caSMauro Carvalho Chehab break;
7380c0d06caSMauro Carvalho Chehab }
7390c0d06caSMauro Carvalho Chehab
7400c0d06caSMauro Carvalho Chehab buflen -= buffer[0];
7410c0d06caSMauro Carvalho Chehab buffer += buffer[0];
7420c0d06caSMauro Carvalho Chehab }
7430c0d06caSMauro Carvalho Chehab
7440c0d06caSMauro Carvalho Chehab if (buflen)
7459e56380aSJoe Perches uvc_dbg(dev, DESCR,
7469e56380aSJoe Perches "device %d videostreaming interface %d has %u bytes of trailing descriptor garbage\n",
7479e56380aSJoe Perches dev->udev->devnum, alts->desc.bInterfaceNumber, buflen);
7480c0d06caSMauro Carvalho Chehab
7490c0d06caSMauro Carvalho Chehab /* Parse the alternate settings to find the maximum bandwidth. */
7500c0d06caSMauro Carvalho Chehab for (i = 0; i < intf->num_altsetting; ++i) {
7510c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep;
7522a8c1952SPedro Guilherme Siqueira Moreira
7530c0d06caSMauro Carvalho Chehab alts = &intf->altsetting[i];
7540c0d06caSMauro Carvalho Chehab ep = uvc_find_endpoint(alts,
7550c0d06caSMauro Carvalho Chehab streaming->header.bEndpointAddress);
7560c0d06caSMauro Carvalho Chehab if (ep == NULL)
7570c0d06caSMauro Carvalho Chehab continue;
7585b9c75c7SRicardo Ribalda psize = uvc_endpoint_max_bpi(dev->udev, ep);
7590c0d06caSMauro Carvalho Chehab if (psize > streaming->maxpsize)
7600c0d06caSMauro Carvalho Chehab streaming->maxpsize = psize;
7610c0d06caSMauro Carvalho Chehab }
7620c0d06caSMauro Carvalho Chehab
7630c0d06caSMauro Carvalho Chehab list_add_tail(&streaming->list, &dev->streams);
7640c0d06caSMauro Carvalho Chehab return 0;
7650c0d06caSMauro Carvalho Chehab
7660c0d06caSMauro Carvalho Chehab error:
7670c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, intf);
768ece41454SKieran Bingham uvc_stream_delete(streaming);
7690c0d06caSMauro Carvalho Chehab return ret;
7700c0d06caSMauro Carvalho Chehab }
7710c0d06caSMauro Carvalho Chehab
772351509c6SRicardo Ribalda static const u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA;
7732886477fSRicardo Ribalda static const u8 uvc_gpio_guid[16] = UVC_GUID_EXT_GPIO_CONTROLLER;
774351509c6SRicardo Ribalda static const u8 uvc_media_transport_input_guid[16] =
775351509c6SRicardo Ribalda UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT;
776351509c6SRicardo Ribalda static const u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING;
777351509c6SRicardo Ribalda
uvc_alloc_new_entity(struct uvc_device * dev,u16 type,u16 id,unsigned int num_pads,unsigned int extra_size)778b11813bcSThadeu Lima de Souza Cascardo static struct uvc_entity *uvc_alloc_new_entity(struct uvc_device *dev, u16 type,
779b11813bcSThadeu Lima de Souza Cascardo u16 id, unsigned int num_pads,
780b11813bcSThadeu Lima de Souza Cascardo unsigned int extra_size)
7810c0d06caSMauro Carvalho Chehab {
7820c0d06caSMauro Carvalho Chehab struct uvc_entity *entity;
7830c0d06caSMauro Carvalho Chehab unsigned int num_inputs;
7840c0d06caSMauro Carvalho Chehab unsigned int size;
7850c0d06caSMauro Carvalho Chehab unsigned int i;
7860c0d06caSMauro Carvalho Chehab
787b11813bcSThadeu Lima de Souza Cascardo /* Per UVC 1.1+ spec 3.7.2, the ID should be non-zero. */
788b11813bcSThadeu Lima de Souza Cascardo if (id == 0) {
789b11813bcSThadeu Lima de Souza Cascardo dev_err(&dev->udev->dev, "Found Unit with invalid ID 0.\n");
790b11813bcSThadeu Lima de Souza Cascardo return ERR_PTR(-EINVAL);
791b11813bcSThadeu Lima de Souza Cascardo }
792b11813bcSThadeu Lima de Souza Cascardo
793b11813bcSThadeu Lima de Souza Cascardo /* Per UVC 1.1+ spec 3.7.2, the ID is unique. */
794b11813bcSThadeu Lima de Souza Cascardo if (uvc_entity_by_id(dev, id)) {
795b11813bcSThadeu Lima de Souza Cascardo dev_err(&dev->udev->dev, "Found multiple Units with ID %u\n", id);
796b11813bcSThadeu Lima de Souza Cascardo return ERR_PTR(-EINVAL);
797b11813bcSThadeu Lima de Souza Cascardo }
798b11813bcSThadeu Lima de Souza Cascardo
79989dd34caSNadav Amit extra_size = roundup(extra_size, sizeof(*entity->pads));
8007532dad6SRicardo Ribalda if (num_pads)
8017532dad6SRicardo Ribalda num_inputs = type & UVC_TERM_OUTPUT ? num_pads : num_pads - 1;
8027532dad6SRicardo Ribalda else
8037532dad6SRicardo Ribalda num_inputs = 0;
8040c0d06caSMauro Carvalho Chehab size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads
8050c0d06caSMauro Carvalho Chehab + num_inputs;
8060c0d06caSMauro Carvalho Chehab entity = kzalloc(size, GFP_KERNEL);
8070c0d06caSMauro Carvalho Chehab if (entity == NULL)
808b11813bcSThadeu Lima de Souza Cascardo return ERR_PTR(-ENOMEM);
8090c0d06caSMauro Carvalho Chehab
8100c0d06caSMauro Carvalho Chehab entity->id = id;
8110c0d06caSMauro Carvalho Chehab entity->type = type;
8120c0d06caSMauro Carvalho Chehab
813351509c6SRicardo Ribalda /*
814351509c6SRicardo Ribalda * Set the GUID for standard entity types. For extension units, the GUID
815351509c6SRicardo Ribalda * is initialized by the caller.
816351509c6SRicardo Ribalda */
817351509c6SRicardo Ribalda switch (type) {
8182886477fSRicardo Ribalda case UVC_EXT_GPIO_UNIT:
8192886477fSRicardo Ribalda memcpy(entity->guid, uvc_gpio_guid, 16);
8202886477fSRicardo Ribalda break;
821351509c6SRicardo Ribalda case UVC_ITT_CAMERA:
822351509c6SRicardo Ribalda memcpy(entity->guid, uvc_camera_guid, 16);
823351509c6SRicardo Ribalda break;
824351509c6SRicardo Ribalda case UVC_ITT_MEDIA_TRANSPORT_INPUT:
825351509c6SRicardo Ribalda memcpy(entity->guid, uvc_media_transport_input_guid, 16);
826351509c6SRicardo Ribalda break;
827351509c6SRicardo Ribalda case UVC_VC_PROCESSING_UNIT:
828351509c6SRicardo Ribalda memcpy(entity->guid, uvc_processing_guid, 16);
829351509c6SRicardo Ribalda break;
830351509c6SRicardo Ribalda }
831351509c6SRicardo Ribalda
8320c0d06caSMauro Carvalho Chehab entity->num_links = 0;
8330c0d06caSMauro Carvalho Chehab entity->num_pads = num_pads;
8340c0d06caSMauro Carvalho Chehab entity->pads = ((void *)(entity + 1)) + extra_size;
8350c0d06caSMauro Carvalho Chehab
8360c0d06caSMauro Carvalho Chehab for (i = 0; i < num_inputs; ++i)
8370c0d06caSMauro Carvalho Chehab entity->pads[i].flags = MEDIA_PAD_FL_SINK;
8387532dad6SRicardo Ribalda if (!UVC_ENTITY_IS_OTERM(entity) && num_pads)
8390c0d06caSMauro Carvalho Chehab entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE;
8400c0d06caSMauro Carvalho Chehab
8410c0d06caSMauro Carvalho Chehab entity->bNrInPins = num_inputs;
8422c6b222cSLaurent Pinchart entity->baSourceID = (u8 *)(&entity->pads[num_pads]);
8430c0d06caSMauro Carvalho Chehab
8440c0d06caSMauro Carvalho Chehab return entity;
8450c0d06caSMauro Carvalho Chehab }
8460c0d06caSMauro Carvalho Chehab
uvc_entity_set_name(struct uvc_device * dev,struct uvc_entity * entity,const char * type_name,u8 string_id)8475f0e659dSLaurent Pinchart static void uvc_entity_set_name(struct uvc_device *dev, struct uvc_entity *entity,
8485f0e659dSLaurent Pinchart const char *type_name, u8 string_id)
8495f0e659dSLaurent Pinchart {
8505f0e659dSLaurent Pinchart int ret;
8515f0e659dSLaurent Pinchart
8525f0e659dSLaurent Pinchart /*
8535f0e659dSLaurent Pinchart * First attempt to read the entity name from the device. If the entity
8545f0e659dSLaurent Pinchart * has no associated string, or if reading the string fails (most
8555f0e659dSLaurent Pinchart * likely due to a buggy firmware), fall back to default names based on
8565f0e659dSLaurent Pinchart * the entity type.
8575f0e659dSLaurent Pinchart */
8585f0e659dSLaurent Pinchart if (string_id) {
8595f0e659dSLaurent Pinchart ret = usb_string(dev->udev, string_id, entity->name,
8605f0e659dSLaurent Pinchart sizeof(entity->name));
8615f0e659dSLaurent Pinchart if (!ret)
8625f0e659dSLaurent Pinchart return;
8635f0e659dSLaurent Pinchart }
8645f0e659dSLaurent Pinchart
8655f0e659dSLaurent Pinchart sprintf(entity->name, "%s %u", type_name, entity->id);
8665f0e659dSLaurent Pinchart }
8675f0e659dSLaurent Pinchart
8680c0d06caSMauro Carvalho Chehab /* Parse vendor-specific extensions. */
uvc_parse_vendor_control(struct uvc_device * dev,const unsigned char * buffer,int buflen)8690c0d06caSMauro Carvalho Chehab static int uvc_parse_vendor_control(struct uvc_device *dev,
8700c0d06caSMauro Carvalho Chehab const unsigned char *buffer, int buflen)
8710c0d06caSMauro Carvalho Chehab {
8720c0d06caSMauro Carvalho Chehab struct usb_device *udev = dev->udev;
8730c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting;
8740c0d06caSMauro Carvalho Chehab struct uvc_entity *unit;
8750c0d06caSMauro Carvalho Chehab unsigned int n, p;
8760c0d06caSMauro Carvalho Chehab int handled = 0;
8770c0d06caSMauro Carvalho Chehab
8780c0d06caSMauro Carvalho Chehab switch (le16_to_cpu(dev->udev->descriptor.idVendor)) {
8790c0d06caSMauro Carvalho Chehab case 0x046d: /* Logitech */
8800c0d06caSMauro Carvalho Chehab if (buffer[1] != 0x41 || buffer[2] != 0x01)
8810c0d06caSMauro Carvalho Chehab break;
8820c0d06caSMauro Carvalho Chehab
883699b9a86SLaurent Pinchart /*
884699b9a86SLaurent Pinchart * Logitech implements several vendor specific functions
8850c0d06caSMauro Carvalho Chehab * through vendor specific extension units (LXU).
8860c0d06caSMauro Carvalho Chehab *
8870c0d06caSMauro Carvalho Chehab * The LXU descriptors are similar to XU descriptors
8880c0d06caSMauro Carvalho Chehab * (see "USB Device Video Class for Video Devices", section
8890c0d06caSMauro Carvalho Chehab * 3.7.2.6 "Extension Unit Descriptor") with the following
8900c0d06caSMauro Carvalho Chehab * differences:
8910c0d06caSMauro Carvalho Chehab *
8920c0d06caSMauro Carvalho Chehab * ----------------------------------------------------------
8930c0d06caSMauro Carvalho Chehab * 0 bLength 1 Number
8940c0d06caSMauro Carvalho Chehab * Size of this descriptor, in bytes: 24+p+n*2
8950c0d06caSMauro Carvalho Chehab * ----------------------------------------------------------
8960c0d06caSMauro Carvalho Chehab * 23+p+n bmControlsType N Bitmap
8970c0d06caSMauro Carvalho Chehab * Individual bits in the set are defined:
8980c0d06caSMauro Carvalho Chehab * 0: Absolute
8990c0d06caSMauro Carvalho Chehab * 1: Relative
9000c0d06caSMauro Carvalho Chehab *
9010c0d06caSMauro Carvalho Chehab * This bitset is mapped exactly the same as bmControls.
9020c0d06caSMauro Carvalho Chehab * ----------------------------------------------------------
9030c0d06caSMauro Carvalho Chehab * 23+p+n*2 bReserved 1 Boolean
9040c0d06caSMauro Carvalho Chehab * ----------------------------------------------------------
9050c0d06caSMauro Carvalho Chehab * 24+p+n*2 iExtension 1 Index
9060c0d06caSMauro Carvalho Chehab * Index of a string descriptor that describes this
9070c0d06caSMauro Carvalho Chehab * extension unit.
9080c0d06caSMauro Carvalho Chehab * ----------------------------------------------------------
9090c0d06caSMauro Carvalho Chehab */
9100c0d06caSMauro Carvalho Chehab p = buflen >= 22 ? buffer[21] : 0;
9110c0d06caSMauro Carvalho Chehab n = buflen >= 25 + p ? buffer[22+p] : 0;
9120c0d06caSMauro Carvalho Chehab
9130c0d06caSMauro Carvalho Chehab if (buflen < 25 + p + 2*n) {
9149e56380aSJoe Perches uvc_dbg(dev, DESCR,
915ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d EXTENSION_UNIT error\n",
9160c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber);
9170c0d06caSMauro Carvalho Chehab break;
9180c0d06caSMauro Carvalho Chehab }
9190c0d06caSMauro Carvalho Chehab
920b11813bcSThadeu Lima de Souza Cascardo unit = uvc_alloc_new_entity(dev, UVC_VC_EXTENSION_UNIT,
921b11813bcSThadeu Lima de Souza Cascardo buffer[3], p + 1, 2 * n);
922b11813bcSThadeu Lima de Souza Cascardo if (IS_ERR(unit))
923b11813bcSThadeu Lima de Souza Cascardo return PTR_ERR(unit);
9240c0d06caSMauro Carvalho Chehab
925351509c6SRicardo Ribalda memcpy(unit->guid, &buffer[4], 16);
9260c0d06caSMauro Carvalho Chehab unit->extension.bNumControls = buffer[20];
9270c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[22], p);
9280c0d06caSMauro Carvalho Chehab unit->extension.bControlSize = buffer[22+p];
9292c6b222cSLaurent Pinchart unit->extension.bmControls = (u8 *)unit + sizeof(*unit);
9302c6b222cSLaurent Pinchart unit->extension.bmControlsType = (u8 *)unit + sizeof(*unit)
9310c0d06caSMauro Carvalho Chehab + n;
9320c0d06caSMauro Carvalho Chehab memcpy(unit->extension.bmControls, &buffer[23+p], 2*n);
9330c0d06caSMauro Carvalho Chehab
9345f0e659dSLaurent Pinchart uvc_entity_set_name(dev, unit, "Extension", buffer[24+p+2*n]);
9350c0d06caSMauro Carvalho Chehab
9360c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities);
9370c0d06caSMauro Carvalho Chehab handled = 1;
9380c0d06caSMauro Carvalho Chehab break;
9390c0d06caSMauro Carvalho Chehab }
9400c0d06caSMauro Carvalho Chehab
9410c0d06caSMauro Carvalho Chehab return handled;
9420c0d06caSMauro Carvalho Chehab }
9430c0d06caSMauro Carvalho Chehab
uvc_parse_standard_control(struct uvc_device * dev,const unsigned char * buffer,int buflen)9440c0d06caSMauro Carvalho Chehab static int uvc_parse_standard_control(struct uvc_device *dev,
9450c0d06caSMauro Carvalho Chehab const unsigned char *buffer, int buflen)
9460c0d06caSMauro Carvalho Chehab {
9470c0d06caSMauro Carvalho Chehab struct usb_device *udev = dev->udev;
9480c0d06caSMauro Carvalho Chehab struct uvc_entity *unit, *term;
9490c0d06caSMauro Carvalho Chehab struct usb_interface *intf;
9500c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting;
9510c0d06caSMauro Carvalho Chehab unsigned int i, n, p, len;
9525f0e659dSLaurent Pinchart const char *type_name;
9532c6b222cSLaurent Pinchart u16 type;
9540c0d06caSMauro Carvalho Chehab
9550c0d06caSMauro Carvalho Chehab switch (buffer[2]) {
9560c0d06caSMauro Carvalho Chehab case UVC_VC_HEADER:
9570c0d06caSMauro Carvalho Chehab n = buflen >= 12 ? buffer[11] : 0;
9580c0d06caSMauro Carvalho Chehab
959daf41ac2SOliver Neukum if (buflen < 12 + n) {
9609e56380aSJoe Perches uvc_dbg(dev, DESCR,
961ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d HEADER error\n",
962ed4c5fa4SRicardo Ribalda udev->devnum, alts->desc.bInterfaceNumber);
9630c0d06caSMauro Carvalho Chehab return -EINVAL;
9640c0d06caSMauro Carvalho Chehab }
9650c0d06caSMauro Carvalho Chehab
9660c0d06caSMauro Carvalho Chehab dev->uvc_version = get_unaligned_le16(&buffer[3]);
9670c0d06caSMauro Carvalho Chehab dev->clock_frequency = get_unaligned_le32(&buffer[7]);
9680c0d06caSMauro Carvalho Chehab
9690c0d06caSMauro Carvalho Chehab /* Parse all USB Video Streaming interfaces. */
9700c0d06caSMauro Carvalho Chehab for (i = 0; i < n; ++i) {
9710c0d06caSMauro Carvalho Chehab intf = usb_ifnum_to_if(udev, buffer[12+i]);
9720c0d06caSMauro Carvalho Chehab if (intf == NULL) {
9739e56380aSJoe Perches uvc_dbg(dev, DESCR,
974ed4c5fa4SRicardo Ribalda "device %d interface %d doesn't exists\n",
9750c0d06caSMauro Carvalho Chehab udev->devnum, i);
9760c0d06caSMauro Carvalho Chehab continue;
9770c0d06caSMauro Carvalho Chehab }
9780c0d06caSMauro Carvalho Chehab
9790c0d06caSMauro Carvalho Chehab uvc_parse_streaming(dev, intf);
9800c0d06caSMauro Carvalho Chehab }
9810c0d06caSMauro Carvalho Chehab break;
9820c0d06caSMauro Carvalho Chehab
9830c0d06caSMauro Carvalho Chehab case UVC_VC_INPUT_TERMINAL:
9840c0d06caSMauro Carvalho Chehab if (buflen < 8) {
9859e56380aSJoe Perches uvc_dbg(dev, DESCR,
986ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d INPUT_TERMINAL error\n",
9870c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber);
9880c0d06caSMauro Carvalho Chehab return -EINVAL;
9890c0d06caSMauro Carvalho Chehab }
9900c0d06caSMauro Carvalho Chehab
99147bb1179SAlistair Strachan /*
99247bb1179SAlistair Strachan * Reject invalid terminal types that would cause issues:
99347bb1179SAlistair Strachan *
99447bb1179SAlistair Strachan * - The high byte must be non-zero, otherwise it would be
99547bb1179SAlistair Strachan * confused with a unit.
99647bb1179SAlistair Strachan *
99747bb1179SAlistair Strachan * - Bit 15 must be 0, as we use it internally as a terminal
99847bb1179SAlistair Strachan * direction flag.
99947bb1179SAlistair Strachan *
100047bb1179SAlistair Strachan * Other unknown types are accepted.
10010c0d06caSMauro Carvalho Chehab */
10020c0d06caSMauro Carvalho Chehab type = get_unaligned_le16(&buffer[4]);
100347bb1179SAlistair Strachan if ((type & 0x7f00) == 0 || (type & 0x8000) != 0) {
10049e56380aSJoe Perches uvc_dbg(dev, DESCR,
1005ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d INPUT_TERMINAL %d has invalid type 0x%04x, skipping\n",
1006ed4c5fa4SRicardo Ribalda udev->devnum, alts->desc.bInterfaceNumber,
10070c0d06caSMauro Carvalho Chehab buffer[3], type);
10080c0d06caSMauro Carvalho Chehab return 0;
10090c0d06caSMauro Carvalho Chehab }
10100c0d06caSMauro Carvalho Chehab
10110c0d06caSMauro Carvalho Chehab n = 0;
10120c0d06caSMauro Carvalho Chehab p = 0;
10130c0d06caSMauro Carvalho Chehab len = 8;
10140c0d06caSMauro Carvalho Chehab
10150c0d06caSMauro Carvalho Chehab if (type == UVC_ITT_CAMERA) {
10160c0d06caSMauro Carvalho Chehab n = buflen >= 15 ? buffer[14] : 0;
10170c0d06caSMauro Carvalho Chehab len = 15;
10180c0d06caSMauro Carvalho Chehab
10190c0d06caSMauro Carvalho Chehab } else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) {
10200c0d06caSMauro Carvalho Chehab n = buflen >= 9 ? buffer[8] : 0;
10210c0d06caSMauro Carvalho Chehab p = buflen >= 10 + n ? buffer[9+n] : 0;
10220c0d06caSMauro Carvalho Chehab len = 10;
10230c0d06caSMauro Carvalho Chehab }
10240c0d06caSMauro Carvalho Chehab
10250c0d06caSMauro Carvalho Chehab if (buflen < len + n + p) {
10269e56380aSJoe Perches uvc_dbg(dev, DESCR,
1027ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d INPUT_TERMINAL error\n",
10280c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber);
10290c0d06caSMauro Carvalho Chehab return -EINVAL;
10300c0d06caSMauro Carvalho Chehab }
10310c0d06caSMauro Carvalho Chehab
1032b11813bcSThadeu Lima de Souza Cascardo term = uvc_alloc_new_entity(dev, type | UVC_TERM_INPUT,
1033b11813bcSThadeu Lima de Souza Cascardo buffer[3], 1, n + p);
1034b11813bcSThadeu Lima de Souza Cascardo if (IS_ERR(term))
1035b11813bcSThadeu Lima de Souza Cascardo return PTR_ERR(term);
10360c0d06caSMauro Carvalho Chehab
10370c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) {
10380c0d06caSMauro Carvalho Chehab term->camera.bControlSize = n;
1039f14d4988SLaurent Pinchart term->camera.bmControls = (u8 *)term + sizeof(*term);
10400c0d06caSMauro Carvalho Chehab term->camera.wObjectiveFocalLengthMin =
10410c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[8]);
10420c0d06caSMauro Carvalho Chehab term->camera.wObjectiveFocalLengthMax =
10430c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[10]);
10440c0d06caSMauro Carvalho Chehab term->camera.wOcularFocalLength =
10450c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[12]);
10460c0d06caSMauro Carvalho Chehab memcpy(term->camera.bmControls, &buffer[15], n);
10470c0d06caSMauro Carvalho Chehab } else if (UVC_ENTITY_TYPE(term) ==
10480c0d06caSMauro Carvalho Chehab UVC_ITT_MEDIA_TRANSPORT_INPUT) {
10490c0d06caSMauro Carvalho Chehab term->media.bControlSize = n;
1050f14d4988SLaurent Pinchart term->media.bmControls = (u8 *)term + sizeof(*term);
10510c0d06caSMauro Carvalho Chehab term->media.bTransportModeSize = p;
10522c6b222cSLaurent Pinchart term->media.bmTransportModes = (u8 *)term
1053f14d4988SLaurent Pinchart + sizeof(*term) + n;
10540c0d06caSMauro Carvalho Chehab memcpy(term->media.bmControls, &buffer[9], n);
10550c0d06caSMauro Carvalho Chehab memcpy(term->media.bmTransportModes, &buffer[10+n], p);
10560c0d06caSMauro Carvalho Chehab }
10570c0d06caSMauro Carvalho Chehab
10584867bb59SGuenter Roeck if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA)
10595f0e659dSLaurent Pinchart type_name = "Camera";
10605f0e659dSLaurent Pinchart else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT)
10615f0e659dSLaurent Pinchart type_name = "Media";
10620c0d06caSMauro Carvalho Chehab else
10635f0e659dSLaurent Pinchart type_name = "Input";
10645f0e659dSLaurent Pinchart
10655f0e659dSLaurent Pinchart uvc_entity_set_name(dev, term, type_name, buffer[7]);
10660c0d06caSMauro Carvalho Chehab
10670c0d06caSMauro Carvalho Chehab list_add_tail(&term->list, &dev->entities);
10680c0d06caSMauro Carvalho Chehab break;
10690c0d06caSMauro Carvalho Chehab
10700c0d06caSMauro Carvalho Chehab case UVC_VC_OUTPUT_TERMINAL:
10710c0d06caSMauro Carvalho Chehab if (buflen < 9) {
10729e56380aSJoe Perches uvc_dbg(dev, DESCR,
1073ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d OUTPUT_TERMINAL error\n",
10740c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber);
10750c0d06caSMauro Carvalho Chehab return -EINVAL;
10760c0d06caSMauro Carvalho Chehab }
10770c0d06caSMauro Carvalho Chehab
1078699b9a86SLaurent Pinchart /*
1079699b9a86SLaurent Pinchart * Make sure the terminal type MSB is not null, otherwise it
10800c0d06caSMauro Carvalho Chehab * could be confused with a unit.
10810c0d06caSMauro Carvalho Chehab */
10820c0d06caSMauro Carvalho Chehab type = get_unaligned_le16(&buffer[4]);
10830c0d06caSMauro Carvalho Chehab if ((type & 0xff00) == 0) {
10849e56380aSJoe Perches uvc_dbg(dev, DESCR,
1085ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d OUTPUT_TERMINAL %d has invalid type 0x%04x, skipping\n",
1086ed4c5fa4SRicardo Ribalda udev->devnum, alts->desc.bInterfaceNumber,
1087ed4c5fa4SRicardo Ribalda buffer[3], type);
10880c0d06caSMauro Carvalho Chehab return 0;
10890c0d06caSMauro Carvalho Chehab }
10900c0d06caSMauro Carvalho Chehab
1091b11813bcSThadeu Lima de Souza Cascardo term = uvc_alloc_new_entity(dev, type | UVC_TERM_OUTPUT,
1092b11813bcSThadeu Lima de Souza Cascardo buffer[3], 1, 0);
1093b11813bcSThadeu Lima de Souza Cascardo if (IS_ERR(term))
1094b11813bcSThadeu Lima de Souza Cascardo return PTR_ERR(term);
10950c0d06caSMauro Carvalho Chehab
10960c0d06caSMauro Carvalho Chehab memcpy(term->baSourceID, &buffer[7], 1);
10970c0d06caSMauro Carvalho Chehab
10985f0e659dSLaurent Pinchart uvc_entity_set_name(dev, term, "Output", buffer[8]);
10990c0d06caSMauro Carvalho Chehab
11000c0d06caSMauro Carvalho Chehab list_add_tail(&term->list, &dev->entities);
11010c0d06caSMauro Carvalho Chehab break;
11020c0d06caSMauro Carvalho Chehab
11030c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT:
11040c0d06caSMauro Carvalho Chehab p = buflen >= 5 ? buffer[4] : 0;
11050c0d06caSMauro Carvalho Chehab
11060c0d06caSMauro Carvalho Chehab if (buflen < 5 || buflen < 6 + p) {
11079e56380aSJoe Perches uvc_dbg(dev, DESCR,
1108ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d SELECTOR_UNIT error\n",
11090c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber);
11100c0d06caSMauro Carvalho Chehab return -EINVAL;
11110c0d06caSMauro Carvalho Chehab }
11120c0d06caSMauro Carvalho Chehab
1113b11813bcSThadeu Lima de Souza Cascardo unit = uvc_alloc_new_entity(dev, buffer[2], buffer[3],
1114b11813bcSThadeu Lima de Souza Cascardo p + 1, 0);
1115b11813bcSThadeu Lima de Souza Cascardo if (IS_ERR(unit))
1116b11813bcSThadeu Lima de Souza Cascardo return PTR_ERR(unit);
11170c0d06caSMauro Carvalho Chehab
11180c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[5], p);
11190c0d06caSMauro Carvalho Chehab
11205f0e659dSLaurent Pinchart uvc_entity_set_name(dev, unit, "Selector", buffer[5+p]);
11210c0d06caSMauro Carvalho Chehab
11220c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities);
11230c0d06caSMauro Carvalho Chehab break;
11240c0d06caSMauro Carvalho Chehab
11250c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT:
11260c0d06caSMauro Carvalho Chehab n = buflen >= 8 ? buffer[7] : 0;
11270c0d06caSMauro Carvalho Chehab p = dev->uvc_version >= 0x0110 ? 10 : 9;
11280c0d06caSMauro Carvalho Chehab
11290c0d06caSMauro Carvalho Chehab if (buflen < p + n) {
11309e56380aSJoe Perches uvc_dbg(dev, DESCR,
1131ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d PROCESSING_UNIT error\n",
11320c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber);
11330c0d06caSMauro Carvalho Chehab return -EINVAL;
11340c0d06caSMauro Carvalho Chehab }
11350c0d06caSMauro Carvalho Chehab
1136b11813bcSThadeu Lima de Souza Cascardo unit = uvc_alloc_new_entity(dev, buffer[2], buffer[3], 2, n);
1137b11813bcSThadeu Lima de Souza Cascardo if (IS_ERR(unit))
1138b11813bcSThadeu Lima de Souza Cascardo return PTR_ERR(unit);
11390c0d06caSMauro Carvalho Chehab
11400c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[4], 1);
11410c0d06caSMauro Carvalho Chehab unit->processing.wMaxMultiplier =
11420c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[5]);
11430c0d06caSMauro Carvalho Chehab unit->processing.bControlSize = buffer[7];
1144f14d4988SLaurent Pinchart unit->processing.bmControls = (u8 *)unit + sizeof(*unit);
11450c0d06caSMauro Carvalho Chehab memcpy(unit->processing.bmControls, &buffer[8], n);
11460c0d06caSMauro Carvalho Chehab if (dev->uvc_version >= 0x0110)
11470c0d06caSMauro Carvalho Chehab unit->processing.bmVideoStandards = buffer[9+n];
11480c0d06caSMauro Carvalho Chehab
11495f0e659dSLaurent Pinchart uvc_entity_set_name(dev, unit, "Processing", buffer[8+n]);
11500c0d06caSMauro Carvalho Chehab
11510c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities);
11520c0d06caSMauro Carvalho Chehab break;
11530c0d06caSMauro Carvalho Chehab
11540c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT:
11550c0d06caSMauro Carvalho Chehab p = buflen >= 22 ? buffer[21] : 0;
11560c0d06caSMauro Carvalho Chehab n = buflen >= 24 + p ? buffer[22+p] : 0;
11570c0d06caSMauro Carvalho Chehab
11580c0d06caSMauro Carvalho Chehab if (buflen < 24 + p + n) {
11599e56380aSJoe Perches uvc_dbg(dev, DESCR,
1160ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d EXTENSION_UNIT error\n",
11610c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber);
11620c0d06caSMauro Carvalho Chehab return -EINVAL;
11630c0d06caSMauro Carvalho Chehab }
11640c0d06caSMauro Carvalho Chehab
1165b11813bcSThadeu Lima de Souza Cascardo unit = uvc_alloc_new_entity(dev, buffer[2], buffer[3],
1166b11813bcSThadeu Lima de Souza Cascardo p + 1, n);
1167b11813bcSThadeu Lima de Souza Cascardo if (IS_ERR(unit))
1168b11813bcSThadeu Lima de Souza Cascardo return PTR_ERR(unit);
11690c0d06caSMauro Carvalho Chehab
1170351509c6SRicardo Ribalda memcpy(unit->guid, &buffer[4], 16);
11710c0d06caSMauro Carvalho Chehab unit->extension.bNumControls = buffer[20];
11720c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[22], p);
11730c0d06caSMauro Carvalho Chehab unit->extension.bControlSize = buffer[22+p];
1174f14d4988SLaurent Pinchart unit->extension.bmControls = (u8 *)unit + sizeof(*unit);
11750c0d06caSMauro Carvalho Chehab memcpy(unit->extension.bmControls, &buffer[23+p], n);
11760c0d06caSMauro Carvalho Chehab
11775f0e659dSLaurent Pinchart uvc_entity_set_name(dev, unit, "Extension", buffer[23+p+n]);
11780c0d06caSMauro Carvalho Chehab
11790c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities);
11800c0d06caSMauro Carvalho Chehab break;
11810c0d06caSMauro Carvalho Chehab
11820c0d06caSMauro Carvalho Chehab default:
11839e56380aSJoe Perches uvc_dbg(dev, DESCR,
1184ed4c5fa4SRicardo Ribalda "Found an unknown CS_INTERFACE descriptor (%u)\n",
1185ed4c5fa4SRicardo Ribalda buffer[2]);
11860c0d06caSMauro Carvalho Chehab break;
11870c0d06caSMauro Carvalho Chehab }
11880c0d06caSMauro Carvalho Chehab
11890c0d06caSMauro Carvalho Chehab return 0;
11900c0d06caSMauro Carvalho Chehab }
11910c0d06caSMauro Carvalho Chehab
uvc_parse_control(struct uvc_device * dev)11920c0d06caSMauro Carvalho Chehab static int uvc_parse_control(struct uvc_device *dev)
11930c0d06caSMauro Carvalho Chehab {
11940c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting;
1195102df33eSLaurent Pinchart const unsigned char *buffer = alts->extra;
11960c0d06caSMauro Carvalho Chehab int buflen = alts->extralen;
11970c0d06caSMauro Carvalho Chehab int ret;
11980c0d06caSMauro Carvalho Chehab
1199699b9a86SLaurent Pinchart /*
1200699b9a86SLaurent Pinchart * Parse the default alternate setting only, as the UVC specification
12010c0d06caSMauro Carvalho Chehab * defines a single alternate setting, the default alternate setting
12020c0d06caSMauro Carvalho Chehab * zero.
12030c0d06caSMauro Carvalho Chehab */
12040c0d06caSMauro Carvalho Chehab
12050c0d06caSMauro Carvalho Chehab while (buflen > 2) {
12060c0d06caSMauro Carvalho Chehab if (uvc_parse_vendor_control(dev, buffer, buflen) ||
12070c0d06caSMauro Carvalho Chehab buffer[1] != USB_DT_CS_INTERFACE)
12080c0d06caSMauro Carvalho Chehab goto next_descriptor;
12090c0d06caSMauro Carvalho Chehab
12107b78a846SPedro Guilherme Siqueira Moreira ret = uvc_parse_standard_control(dev, buffer, buflen);
12117b78a846SPedro Guilherme Siqueira Moreira if (ret < 0)
12120c0d06caSMauro Carvalho Chehab return ret;
12130c0d06caSMauro Carvalho Chehab
12140c0d06caSMauro Carvalho Chehab next_descriptor:
12150c0d06caSMauro Carvalho Chehab buflen -= buffer[0];
12160c0d06caSMauro Carvalho Chehab buffer += buffer[0];
12170c0d06caSMauro Carvalho Chehab }
12180c0d06caSMauro Carvalho Chehab
1219699b9a86SLaurent Pinchart /*
1220699b9a86SLaurent Pinchart * Check if the optional status endpoint is present. Built-in iSight
12210c0d06caSMauro Carvalho Chehab * webcams have an interrupt endpoint but spit proprietary data that
12220c0d06caSMauro Carvalho Chehab * don't conform to the UVC status endpoint messages. Don't try to
12230c0d06caSMauro Carvalho Chehab * handle the interrupt endpoint for those cameras.
12240c0d06caSMauro Carvalho Chehab */
12250c0d06caSMauro Carvalho Chehab if (alts->desc.bNumEndpoints == 1 &&
12260c0d06caSMauro Carvalho Chehab !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) {
12270c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep = &alts->endpoint[0];
12280c0d06caSMauro Carvalho Chehab struct usb_endpoint_descriptor *desc = &ep->desc;
12290c0d06caSMauro Carvalho Chehab
12300c0d06caSMauro Carvalho Chehab if (usb_endpoint_is_int_in(desc) &&
12310c0d06caSMauro Carvalho Chehab le16_to_cpu(desc->wMaxPacketSize) >= 8 &&
12320c0d06caSMauro Carvalho Chehab desc->bInterval != 0) {
12339e56380aSJoe Perches uvc_dbg(dev, DESCR,
12349e56380aSJoe Perches "Found a Status endpoint (addr %02x)\n",
1235ed4c5fa4SRicardo Ribalda desc->bEndpointAddress);
12360c0d06caSMauro Carvalho Chehab dev->int_ep = ep;
12370c0d06caSMauro Carvalho Chehab }
12380c0d06caSMauro Carvalho Chehab }
12390c0d06caSMauro Carvalho Chehab
12400c0d06caSMauro Carvalho Chehab return 0;
12410c0d06caSMauro Carvalho Chehab }
12420c0d06caSMauro Carvalho Chehab
12432886477fSRicardo Ribalda /* -----------------------------------------------------------------------------
12442886477fSRicardo Ribalda * Privacy GPIO
12452886477fSRicardo Ribalda */
12462886477fSRicardo Ribalda
uvc_gpio_event(struct uvc_device * dev)12472886477fSRicardo Ribalda static void uvc_gpio_event(struct uvc_device *dev)
12482886477fSRicardo Ribalda {
12492886477fSRicardo Ribalda struct uvc_entity *unit = dev->gpio_unit;
12502886477fSRicardo Ribalda struct uvc_video_chain *chain;
12512886477fSRicardo Ribalda u8 new_val;
12522886477fSRicardo Ribalda
12532886477fSRicardo Ribalda if (!unit)
12542886477fSRicardo Ribalda return;
12552886477fSRicardo Ribalda
12562886477fSRicardo Ribalda new_val = gpiod_get_value_cansleep(unit->gpio.gpio_privacy);
12572886477fSRicardo Ribalda
12582886477fSRicardo Ribalda /* GPIO entities are always on the first chain. */
12592886477fSRicardo Ribalda chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
12602886477fSRicardo Ribalda uvc_ctrl_status_event(chain, unit->controls, &new_val);
12612886477fSRicardo Ribalda }
12622886477fSRicardo Ribalda
uvc_gpio_get_cur(struct uvc_device * dev,struct uvc_entity * entity,u8 cs,void * data,u16 size)12632886477fSRicardo Ribalda static int uvc_gpio_get_cur(struct uvc_device *dev, struct uvc_entity *entity,
12642886477fSRicardo Ribalda u8 cs, void *data, u16 size)
12652886477fSRicardo Ribalda {
12662886477fSRicardo Ribalda if (cs != UVC_CT_PRIVACY_CONTROL || size < 1)
12672886477fSRicardo Ribalda return -EINVAL;
12682886477fSRicardo Ribalda
12692886477fSRicardo Ribalda *(u8 *)data = gpiod_get_value_cansleep(entity->gpio.gpio_privacy);
12702886477fSRicardo Ribalda
12712886477fSRicardo Ribalda return 0;
12722886477fSRicardo Ribalda }
12732886477fSRicardo Ribalda
uvc_gpio_get_info(struct uvc_device * dev,struct uvc_entity * entity,u8 cs,u8 * caps)12742886477fSRicardo Ribalda static int uvc_gpio_get_info(struct uvc_device *dev, struct uvc_entity *entity,
12752886477fSRicardo Ribalda u8 cs, u8 *caps)
12762886477fSRicardo Ribalda {
12772886477fSRicardo Ribalda if (cs != UVC_CT_PRIVACY_CONTROL)
12782886477fSRicardo Ribalda return -EINVAL;
12792886477fSRicardo Ribalda
12802886477fSRicardo Ribalda *caps = UVC_CONTROL_CAP_GET | UVC_CONTROL_CAP_AUTOUPDATE;
12812886477fSRicardo Ribalda return 0;
12822886477fSRicardo Ribalda }
12832886477fSRicardo Ribalda
uvc_gpio_irq(int irq,void * data)12842886477fSRicardo Ribalda static irqreturn_t uvc_gpio_irq(int irq, void *data)
12852886477fSRicardo Ribalda {
12862886477fSRicardo Ribalda struct uvc_device *dev = data;
12872886477fSRicardo Ribalda
12882886477fSRicardo Ribalda uvc_gpio_event(dev);
12892886477fSRicardo Ribalda return IRQ_HANDLED;
12902886477fSRicardo Ribalda }
12912886477fSRicardo Ribalda
uvc_gpio_parse(struct uvc_device * dev)12922886477fSRicardo Ribalda static int uvc_gpio_parse(struct uvc_device *dev)
12932886477fSRicardo Ribalda {
12942886477fSRicardo Ribalda struct uvc_entity *unit;
12952886477fSRicardo Ribalda struct gpio_desc *gpio_privacy;
12962886477fSRicardo Ribalda int irq;
12972886477fSRicardo Ribalda
12982886477fSRicardo Ribalda gpio_privacy = devm_gpiod_get_optional(&dev->udev->dev, "privacy",
12992886477fSRicardo Ribalda GPIOD_IN);
13002886477fSRicardo Ribalda if (IS_ERR_OR_NULL(gpio_privacy))
13012886477fSRicardo Ribalda return PTR_ERR_OR_ZERO(gpio_privacy);
13022886477fSRicardo Ribalda
13032886477fSRicardo Ribalda irq = gpiod_to_irq(gpio_privacy);
13046cb7d1b3SYang Yingliang if (irq < 0)
13056cb7d1b3SYang Yingliang return dev_err_probe(&dev->udev->dev, irq,
13066cb7d1b3SYang Yingliang "No IRQ for privacy GPIO\n");
13072886477fSRicardo Ribalda
1308b11813bcSThadeu Lima de Souza Cascardo unit = uvc_alloc_new_entity(dev, UVC_EXT_GPIO_UNIT,
1309b11813bcSThadeu Lima de Souza Cascardo UVC_EXT_GPIO_UNIT_ID, 0, 1);
1310b11813bcSThadeu Lima de Souza Cascardo if (IS_ERR(unit))
1311b11813bcSThadeu Lima de Souza Cascardo return PTR_ERR(unit);
1312f0f07845SJosé Expósito
13132886477fSRicardo Ribalda unit->gpio.gpio_privacy = gpio_privacy;
13142886477fSRicardo Ribalda unit->gpio.irq = irq;
13152886477fSRicardo Ribalda unit->gpio.bControlSize = 1;
13162886477fSRicardo Ribalda unit->gpio.bmControls = (u8 *)unit + sizeof(*unit);
13172886477fSRicardo Ribalda unit->gpio.bmControls[0] = 1;
13182886477fSRicardo Ribalda unit->get_cur = uvc_gpio_get_cur;
13192886477fSRicardo Ribalda unit->get_info = uvc_gpio_get_info;
1320063b811fSHans Verkuil strscpy(unit->name, "GPIO", sizeof(unit->name));
13212886477fSRicardo Ribalda
13222886477fSRicardo Ribalda list_add_tail(&unit->list, &dev->entities);
13232886477fSRicardo Ribalda
13242886477fSRicardo Ribalda dev->gpio_unit = unit;
13252886477fSRicardo Ribalda
13262886477fSRicardo Ribalda return 0;
13272886477fSRicardo Ribalda }
13282886477fSRicardo Ribalda
uvc_gpio_init_irq(struct uvc_device * dev)13292886477fSRicardo Ribalda static int uvc_gpio_init_irq(struct uvc_device *dev)
13302886477fSRicardo Ribalda {
13312886477fSRicardo Ribalda struct uvc_entity *unit = dev->gpio_unit;
13322886477fSRicardo Ribalda
13332886477fSRicardo Ribalda if (!unit || unit->gpio.irq < 0)
13342886477fSRicardo Ribalda return 0;
13352886477fSRicardo Ribalda
13362886477fSRicardo Ribalda return devm_request_threaded_irq(&dev->udev->dev, unit->gpio.irq, NULL,
13372886477fSRicardo Ribalda uvc_gpio_irq,
13382886477fSRicardo Ribalda IRQF_ONESHOT | IRQF_TRIGGER_FALLING |
13392886477fSRicardo Ribalda IRQF_TRIGGER_RISING,
13402886477fSRicardo Ribalda "uvc_privacy_gpio", dev);
13412886477fSRicardo Ribalda }
13422886477fSRicardo Ribalda
13430c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
13440c0d06caSMauro Carvalho Chehab * UVC device scan
13450c0d06caSMauro Carvalho Chehab */
13460c0d06caSMauro Carvalho Chehab
13470c0d06caSMauro Carvalho Chehab /*
13480c0d06caSMauro Carvalho Chehab * Scan the UVC descriptors to locate a chain starting at an Output Terminal
13490c0d06caSMauro Carvalho Chehab * and containing the following units:
13500c0d06caSMauro Carvalho Chehab *
13510c0d06caSMauro Carvalho Chehab * - one or more Output Terminals (USB Streaming or Display)
13520c0d06caSMauro Carvalho Chehab * - zero or one Processing Unit
13530c0d06caSMauro Carvalho Chehab * - zero, one or more single-input Selector Units
13540c0d06caSMauro Carvalho Chehab * - zero or one multiple-input Selector Units, provided all inputs are
13550c0d06caSMauro Carvalho Chehab * connected to input terminals
13560c0d06caSMauro Carvalho Chehab * - zero, one or mode single-input Extension Units
13570c0d06caSMauro Carvalho Chehab * - one or more Input Terminals (Camera, External or USB Streaming)
13580c0d06caSMauro Carvalho Chehab *
13590c0d06caSMauro Carvalho Chehab * The terminal and units must match on of the following structures:
13600c0d06caSMauro Carvalho Chehab *
13610c0d06caSMauro Carvalho Chehab * ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0)
13620c0d06caSMauro Carvalho Chehab * ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ...
13630c0d06caSMauro Carvalho Chehab * ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n)
13640c0d06caSMauro Carvalho Chehab *
13650c0d06caSMauro Carvalho Chehab * +---------+ +---------+ -> OTT_*(0)
13660c0d06caSMauro Carvalho Chehab * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ...
13670c0d06caSMauro Carvalho Chehab * +---------+ +---------+ -> OTT_*(n)
13680c0d06caSMauro Carvalho Chehab *
13690c0d06caSMauro Carvalho Chehab * The Processing Unit and Extension Units can be in any order. Additional
13700c0d06caSMauro Carvalho Chehab * Extension Units connected to the main chain as single-unit branches are
13710c0d06caSMauro Carvalho Chehab * also supported. Single-input Selector Units are ignored.
13720c0d06caSMauro Carvalho Chehab */
uvc_scan_chain_entity(struct uvc_video_chain * chain,struct uvc_entity * entity)13730c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_entity(struct uvc_video_chain *chain,
13740c0d06caSMauro Carvalho Chehab struct uvc_entity *entity)
13750c0d06caSMauro Carvalho Chehab {
13760c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(entity)) {
13770c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT:
13789e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- XU %d", entity->id);
13790c0d06caSMauro Carvalho Chehab
13800c0d06caSMauro Carvalho Chehab if (entity->bNrInPins != 1) {
13819e56380aSJoe Perches uvc_dbg(chain->dev, DESCR,
13829e56380aSJoe Perches "Extension unit %d has more than 1 input pin\n",
1383ed4c5fa4SRicardo Ribalda entity->id);
13840c0d06caSMauro Carvalho Chehab return -1;
13850c0d06caSMauro Carvalho Chehab }
13860c0d06caSMauro Carvalho Chehab
13870c0d06caSMauro Carvalho Chehab break;
13880c0d06caSMauro Carvalho Chehab
13890c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT:
13909e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- PU %d", entity->id);
13910c0d06caSMauro Carvalho Chehab
13920c0d06caSMauro Carvalho Chehab if (chain->processing != NULL) {
13939e56380aSJoe Perches uvc_dbg(chain->dev, DESCR,
13949e56380aSJoe Perches "Found multiple Processing Units in chain\n");
13950c0d06caSMauro Carvalho Chehab return -1;
13960c0d06caSMauro Carvalho Chehab }
13970c0d06caSMauro Carvalho Chehab
13980c0d06caSMauro Carvalho Chehab chain->processing = entity;
13990c0d06caSMauro Carvalho Chehab break;
14000c0d06caSMauro Carvalho Chehab
14010c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT:
14029e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- SU %d", entity->id);
14030c0d06caSMauro Carvalho Chehab
14040c0d06caSMauro Carvalho Chehab /* Single-input selector units are ignored. */
14050c0d06caSMauro Carvalho Chehab if (entity->bNrInPins == 1)
14060c0d06caSMauro Carvalho Chehab break;
14070c0d06caSMauro Carvalho Chehab
14080c0d06caSMauro Carvalho Chehab if (chain->selector != NULL) {
14099e56380aSJoe Perches uvc_dbg(chain->dev, DESCR,
14109e56380aSJoe Perches "Found multiple Selector Units in chain\n");
14110c0d06caSMauro Carvalho Chehab return -1;
14120c0d06caSMauro Carvalho Chehab }
14130c0d06caSMauro Carvalho Chehab
14140c0d06caSMauro Carvalho Chehab chain->selector = entity;
14150c0d06caSMauro Carvalho Chehab break;
14160c0d06caSMauro Carvalho Chehab
14170c0d06caSMauro Carvalho Chehab case UVC_ITT_VENDOR_SPECIFIC:
14180c0d06caSMauro Carvalho Chehab case UVC_ITT_CAMERA:
14190c0d06caSMauro Carvalho Chehab case UVC_ITT_MEDIA_TRANSPORT_INPUT:
14209e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- IT %d\n", entity->id);
14210c0d06caSMauro Carvalho Chehab
14220c0d06caSMauro Carvalho Chehab break;
14230c0d06caSMauro Carvalho Chehab
14240c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC:
14250c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY:
14260c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
14279e56380aSJoe Perches uvc_dbg_cont(PROBE, " OT %d", entity->id);
14280c0d06caSMauro Carvalho Chehab
14290c0d06caSMauro Carvalho Chehab break;
14300c0d06caSMauro Carvalho Chehab
14310c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING:
143259e92bf6SRicardo Ribalda if (UVC_ENTITY_IS_ITERM(entity))
14339e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- IT %d\n", entity->id);
143459e92bf6SRicardo Ribalda else
14359e56380aSJoe Perches uvc_dbg_cont(PROBE, " OT %d", entity->id);
14360c0d06caSMauro Carvalho Chehab
14370c0d06caSMauro Carvalho Chehab break;
14380c0d06caSMauro Carvalho Chehab
14390c0d06caSMauro Carvalho Chehab default:
14409e56380aSJoe Perches uvc_dbg(chain->dev, DESCR,
14419e56380aSJoe Perches "Unsupported entity type 0x%04x found in chain\n",
1442ed4c5fa4SRicardo Ribalda UVC_ENTITY_TYPE(entity));
14430c0d06caSMauro Carvalho Chehab return -1;
14440c0d06caSMauro Carvalho Chehab }
14450c0d06caSMauro Carvalho Chehab
14460c0d06caSMauro Carvalho Chehab list_add_tail(&entity->chain, &chain->entities);
14470c0d06caSMauro Carvalho Chehab return 0;
14480c0d06caSMauro Carvalho Chehab }
14490c0d06caSMauro Carvalho Chehab
uvc_scan_chain_forward(struct uvc_video_chain * chain,struct uvc_entity * entity,struct uvc_entity * prev)14500c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_forward(struct uvc_video_chain *chain,
14510c0d06caSMauro Carvalho Chehab struct uvc_entity *entity, struct uvc_entity *prev)
14520c0d06caSMauro Carvalho Chehab {
14530c0d06caSMauro Carvalho Chehab struct uvc_entity *forward;
14540c0d06caSMauro Carvalho Chehab int found;
14550c0d06caSMauro Carvalho Chehab
14560c0d06caSMauro Carvalho Chehab /* Forward scan */
14570c0d06caSMauro Carvalho Chehab forward = NULL;
14580c0d06caSMauro Carvalho Chehab found = 0;
14590c0d06caSMauro Carvalho Chehab
14600c0d06caSMauro Carvalho Chehab while (1) {
14610c0d06caSMauro Carvalho Chehab forward = uvc_entity_by_reference(chain->dev, entity->id,
14620c0d06caSMauro Carvalho Chehab forward);
14630c0d06caSMauro Carvalho Chehab if (forward == NULL)
14640c0d06caSMauro Carvalho Chehab break;
14650c0d06caSMauro Carvalho Chehab if (forward == prev)
14660c0d06caSMauro Carvalho Chehab continue;
146768035c80SWill Deacon if (forward->chain.next || forward->chain.prev) {
14689e56380aSJoe Perches uvc_dbg(chain->dev, DESCR,
14699e56380aSJoe Perches "Found reference to entity %d already in chain\n",
1470ed4c5fa4SRicardo Ribalda forward->id);
147168035c80SWill Deacon return -EINVAL;
147268035c80SWill Deacon }
14730c0d06caSMauro Carvalho Chehab
14740c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(forward)) {
14750c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT:
14760c0d06caSMauro Carvalho Chehab if (forward->bNrInPins != 1) {
14779e56380aSJoe Perches uvc_dbg(chain->dev, DESCR,
14789e56380aSJoe Perches "Extension unit %d has more than 1 input pin\n",
147932934486SLaurent Pinchart forward->id);
14800c0d06caSMauro Carvalho Chehab return -EINVAL;
14810c0d06caSMauro Carvalho Chehab }
14820c0d06caSMauro Carvalho Chehab
14834ca052b4SLaurent Pinchart /*
14844ca052b4SLaurent Pinchart * Some devices reference an output terminal as the
14854ca052b4SLaurent Pinchart * source of extension units. This is incorrect, as
14864ca052b4SLaurent Pinchart * output terminals only have an input pin, and thus
14874ca052b4SLaurent Pinchart * can't be connected to any entity in the forward
14884ca052b4SLaurent Pinchart * direction. The resulting topology would cause issues
14894ca052b4SLaurent Pinchart * when registering the media controller graph. To
14904ca052b4SLaurent Pinchart * avoid this problem, connect the extension unit to
14914ca052b4SLaurent Pinchart * the source of the output terminal instead.
14924ca052b4SLaurent Pinchart */
14934ca052b4SLaurent Pinchart if (UVC_ENTITY_IS_OTERM(entity)) {
14944ca052b4SLaurent Pinchart struct uvc_entity *source;
14954ca052b4SLaurent Pinchart
14964ca052b4SLaurent Pinchart source = uvc_entity_by_id(chain->dev,
14974ca052b4SLaurent Pinchart entity->baSourceID[0]);
14984ca052b4SLaurent Pinchart if (!source) {
14994ca052b4SLaurent Pinchart uvc_dbg(chain->dev, DESCR,
15004ca052b4SLaurent Pinchart "Can't connect extension unit %u in chain\n",
15014ca052b4SLaurent Pinchart forward->id);
15024ca052b4SLaurent Pinchart break;
15034ca052b4SLaurent Pinchart }
15044ca052b4SLaurent Pinchart
15054ca052b4SLaurent Pinchart forward->baSourceID[0] = source->id;
15064ca052b4SLaurent Pinchart }
15074ca052b4SLaurent Pinchart
15080c0d06caSMauro Carvalho Chehab list_add_tail(&forward->chain, &chain->entities);
15090c0d06caSMauro Carvalho Chehab if (!found)
15109e56380aSJoe Perches uvc_dbg_cont(PROBE, " (->");
15110c0d06caSMauro Carvalho Chehab
15129e56380aSJoe Perches uvc_dbg_cont(PROBE, " XU %d", forward->id);
15130c0d06caSMauro Carvalho Chehab found = 1;
15140c0d06caSMauro Carvalho Chehab break;
15150c0d06caSMauro Carvalho Chehab
15160c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC:
15170c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY:
15180c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
15190c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING:
15200c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_IS_ITERM(forward)) {
15219e56380aSJoe Perches uvc_dbg(chain->dev, DESCR,
15229e56380aSJoe Perches "Unsupported input terminal %u\n",
1523ed4c5fa4SRicardo Ribalda forward->id);
15240c0d06caSMauro Carvalho Chehab return -EINVAL;
15250c0d06caSMauro Carvalho Chehab }
15260c0d06caSMauro Carvalho Chehab
15274ca052b4SLaurent Pinchart if (UVC_ENTITY_IS_OTERM(entity)) {
15284ca052b4SLaurent Pinchart uvc_dbg(chain->dev, DESCR,
15294ca052b4SLaurent Pinchart "Unsupported connection between output terminals %u and %u\n",
15304ca052b4SLaurent Pinchart entity->id, forward->id);
15314ca052b4SLaurent Pinchart break;
15324ca052b4SLaurent Pinchart }
15334ca052b4SLaurent Pinchart
15340c0d06caSMauro Carvalho Chehab list_add_tail(&forward->chain, &chain->entities);
15350c0d06caSMauro Carvalho Chehab if (!found)
15369e56380aSJoe Perches uvc_dbg_cont(PROBE, " (->");
15370c0d06caSMauro Carvalho Chehab
15389e56380aSJoe Perches uvc_dbg_cont(PROBE, " OT %d", forward->id);
15390c0d06caSMauro Carvalho Chehab found = 1;
15400c0d06caSMauro Carvalho Chehab break;
15410c0d06caSMauro Carvalho Chehab }
15420c0d06caSMauro Carvalho Chehab }
15430c0d06caSMauro Carvalho Chehab if (found)
15449e56380aSJoe Perches uvc_dbg_cont(PROBE, ")");
15450c0d06caSMauro Carvalho Chehab
15460c0d06caSMauro Carvalho Chehab return 0;
15470c0d06caSMauro Carvalho Chehab }
15480c0d06caSMauro Carvalho Chehab
uvc_scan_chain_backward(struct uvc_video_chain * chain,struct uvc_entity ** _entity)15490c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_backward(struct uvc_video_chain *chain,
15500c0d06caSMauro Carvalho Chehab struct uvc_entity **_entity)
15510c0d06caSMauro Carvalho Chehab {
15520c0d06caSMauro Carvalho Chehab struct uvc_entity *entity = *_entity;
15530c0d06caSMauro Carvalho Chehab struct uvc_entity *term;
15540c0d06caSMauro Carvalho Chehab int id = -EINVAL, i;
15550c0d06caSMauro Carvalho Chehab
15560c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(entity)) {
15570c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT:
15580c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT:
15590c0d06caSMauro Carvalho Chehab id = entity->baSourceID[0];
15600c0d06caSMauro Carvalho Chehab break;
15610c0d06caSMauro Carvalho Chehab
15620c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT:
15630c0d06caSMauro Carvalho Chehab /* Single-input selector units are ignored. */
15640c0d06caSMauro Carvalho Chehab if (entity->bNrInPins == 1) {
15650c0d06caSMauro Carvalho Chehab id = entity->baSourceID[0];
15660c0d06caSMauro Carvalho Chehab break;
15670c0d06caSMauro Carvalho Chehab }
15680c0d06caSMauro Carvalho Chehab
15699e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- IT");
15700c0d06caSMauro Carvalho Chehab
15710c0d06caSMauro Carvalho Chehab chain->selector = entity;
15720c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->bNrInPins; ++i) {
15730c0d06caSMauro Carvalho Chehab id = entity->baSourceID[i];
15740c0d06caSMauro Carvalho Chehab term = uvc_entity_by_id(chain->dev, id);
15750c0d06caSMauro Carvalho Chehab if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
15769e56380aSJoe Perches uvc_dbg(chain->dev, DESCR,
1577ed4c5fa4SRicardo Ribalda "Selector unit %d input %d isn't connected to an input terminal\n",
1578ed4c5fa4SRicardo Ribalda entity->id, i);
15790c0d06caSMauro Carvalho Chehab return -1;
15800c0d06caSMauro Carvalho Chehab }
15810c0d06caSMauro Carvalho Chehab
158268035c80SWill Deacon if (term->chain.next || term->chain.prev) {
15839e56380aSJoe Perches uvc_dbg(chain->dev, DESCR,
15849e56380aSJoe Perches "Found reference to entity %d already in chain\n",
158568035c80SWill Deacon term->id);
158668035c80SWill Deacon return -EINVAL;
158768035c80SWill Deacon }
158868035c80SWill Deacon
15899e56380aSJoe Perches uvc_dbg_cont(PROBE, " %d", term->id);
15900c0d06caSMauro Carvalho Chehab
15910c0d06caSMauro Carvalho Chehab list_add_tail(&term->chain, &chain->entities);
15920c0d06caSMauro Carvalho Chehab uvc_scan_chain_forward(chain, term, entity);
15930c0d06caSMauro Carvalho Chehab }
15940c0d06caSMauro Carvalho Chehab
15959e56380aSJoe Perches uvc_dbg_cont(PROBE, "\n");
15960c0d06caSMauro Carvalho Chehab
15970c0d06caSMauro Carvalho Chehab id = 0;
15980c0d06caSMauro Carvalho Chehab break;
15990c0d06caSMauro Carvalho Chehab
16000c0d06caSMauro Carvalho Chehab case UVC_ITT_VENDOR_SPECIFIC:
16010c0d06caSMauro Carvalho Chehab case UVC_ITT_CAMERA:
16020c0d06caSMauro Carvalho Chehab case UVC_ITT_MEDIA_TRANSPORT_INPUT:
16030c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC:
16040c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY:
16050c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
16060c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING:
16070c0d06caSMauro Carvalho Chehab id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0;
16080c0d06caSMauro Carvalho Chehab break;
16090c0d06caSMauro Carvalho Chehab }
16100c0d06caSMauro Carvalho Chehab
16110c0d06caSMauro Carvalho Chehab if (id <= 0) {
16120c0d06caSMauro Carvalho Chehab *_entity = NULL;
16130c0d06caSMauro Carvalho Chehab return id;
16140c0d06caSMauro Carvalho Chehab }
16150c0d06caSMauro Carvalho Chehab
16160c0d06caSMauro Carvalho Chehab entity = uvc_entity_by_id(chain->dev, id);
16170c0d06caSMauro Carvalho Chehab if (entity == NULL) {
16189e56380aSJoe Perches uvc_dbg(chain->dev, DESCR,
16199e56380aSJoe Perches "Found reference to unknown entity %d\n", id);
16200c0d06caSMauro Carvalho Chehab return -EINVAL;
16210c0d06caSMauro Carvalho Chehab }
16220c0d06caSMauro Carvalho Chehab
16230c0d06caSMauro Carvalho Chehab *_entity = entity;
16240c0d06caSMauro Carvalho Chehab return 0;
16250c0d06caSMauro Carvalho Chehab }
16260c0d06caSMauro Carvalho Chehab
uvc_scan_chain(struct uvc_video_chain * chain,struct uvc_entity * term)16270c0d06caSMauro Carvalho Chehab static int uvc_scan_chain(struct uvc_video_chain *chain,
16280c0d06caSMauro Carvalho Chehab struct uvc_entity *term)
16290c0d06caSMauro Carvalho Chehab {
16300c0d06caSMauro Carvalho Chehab struct uvc_entity *entity, *prev;
16310c0d06caSMauro Carvalho Chehab
16329e56380aSJoe Perches uvc_dbg(chain->dev, PROBE, "Scanning UVC chain:");
16330c0d06caSMauro Carvalho Chehab
16340c0d06caSMauro Carvalho Chehab entity = term;
16350c0d06caSMauro Carvalho Chehab prev = NULL;
16360c0d06caSMauro Carvalho Chehab
16370c0d06caSMauro Carvalho Chehab while (entity != NULL) {
16380c0d06caSMauro Carvalho Chehab /* Entity must not be part of an existing chain */
16390c0d06caSMauro Carvalho Chehab if (entity->chain.next || entity->chain.prev) {
16409e56380aSJoe Perches uvc_dbg(chain->dev, DESCR,
16419e56380aSJoe Perches "Found reference to entity %d already in chain\n",
1642ed4c5fa4SRicardo Ribalda entity->id);
16430c0d06caSMauro Carvalho Chehab return -EINVAL;
16440c0d06caSMauro Carvalho Chehab }
16450c0d06caSMauro Carvalho Chehab
16460c0d06caSMauro Carvalho Chehab /* Process entity */
16470c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_entity(chain, entity) < 0)
16480c0d06caSMauro Carvalho Chehab return -EINVAL;
16490c0d06caSMauro Carvalho Chehab
16500c0d06caSMauro Carvalho Chehab /* Forward scan */
16510c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_forward(chain, entity, prev) < 0)
16520c0d06caSMauro Carvalho Chehab return -EINVAL;
16530c0d06caSMauro Carvalho Chehab
16540c0d06caSMauro Carvalho Chehab /* Backward scan */
16550c0d06caSMauro Carvalho Chehab prev = entity;
16560c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_backward(chain, &entity) < 0)
16570c0d06caSMauro Carvalho Chehab return -EINVAL;
16580c0d06caSMauro Carvalho Chehab }
16590c0d06caSMauro Carvalho Chehab
16600c0d06caSMauro Carvalho Chehab return 0;
16610c0d06caSMauro Carvalho Chehab }
16620c0d06caSMauro Carvalho Chehab
uvc_print_terms(struct list_head * terms,u16 dir,char * buffer)16630c0d06caSMauro Carvalho Chehab static unsigned int uvc_print_terms(struct list_head *terms, u16 dir,
16640c0d06caSMauro Carvalho Chehab char *buffer)
16650c0d06caSMauro Carvalho Chehab {
16660c0d06caSMauro Carvalho Chehab struct uvc_entity *term;
16670c0d06caSMauro Carvalho Chehab unsigned int nterms = 0;
16680c0d06caSMauro Carvalho Chehab char *p = buffer;
16690c0d06caSMauro Carvalho Chehab
16700c0d06caSMauro Carvalho Chehab list_for_each_entry(term, terms, chain) {
16710c0d06caSMauro Carvalho Chehab if (!UVC_ENTITY_IS_TERM(term) ||
16720c0d06caSMauro Carvalho Chehab UVC_TERM_DIRECTION(term) != dir)
16730c0d06caSMauro Carvalho Chehab continue;
16740c0d06caSMauro Carvalho Chehab
16750c0d06caSMauro Carvalho Chehab if (nterms)
16760c0d06caSMauro Carvalho Chehab p += sprintf(p, ",");
16770c0d06caSMauro Carvalho Chehab if (++nterms >= 4) {
16780c0d06caSMauro Carvalho Chehab p += sprintf(p, "...");
16790c0d06caSMauro Carvalho Chehab break;
16800c0d06caSMauro Carvalho Chehab }
16810c0d06caSMauro Carvalho Chehab p += sprintf(p, "%u", term->id);
16820c0d06caSMauro Carvalho Chehab }
16830c0d06caSMauro Carvalho Chehab
16840c0d06caSMauro Carvalho Chehab return p - buffer;
16850c0d06caSMauro Carvalho Chehab }
16860c0d06caSMauro Carvalho Chehab
uvc_print_chain(struct uvc_video_chain * chain)16870c0d06caSMauro Carvalho Chehab static const char *uvc_print_chain(struct uvc_video_chain *chain)
16880c0d06caSMauro Carvalho Chehab {
16890c0d06caSMauro Carvalho Chehab static char buffer[43];
16900c0d06caSMauro Carvalho Chehab char *p = buffer;
16910c0d06caSMauro Carvalho Chehab
16920c0d06caSMauro Carvalho Chehab p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p);
16930c0d06caSMauro Carvalho Chehab p += sprintf(p, " -> ");
16940c0d06caSMauro Carvalho Chehab uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p);
16950c0d06caSMauro Carvalho Chehab
16960c0d06caSMauro Carvalho Chehab return buffer;
16970c0d06caSMauro Carvalho Chehab }
16980c0d06caSMauro Carvalho Chehab
uvc_alloc_chain(struct uvc_device * dev)1699e950267aSHenrik Ingo static struct uvc_video_chain *uvc_alloc_chain(struct uvc_device *dev)
1700e950267aSHenrik Ingo {
1701e950267aSHenrik Ingo struct uvc_video_chain *chain;
1702e950267aSHenrik Ingo
1703e950267aSHenrik Ingo chain = kzalloc(sizeof(*chain), GFP_KERNEL);
1704e950267aSHenrik Ingo if (chain == NULL)
1705e950267aSHenrik Ingo return NULL;
1706e950267aSHenrik Ingo
1707e950267aSHenrik Ingo INIT_LIST_HEAD(&chain->entities);
1708e950267aSHenrik Ingo mutex_init(&chain->ctrl_mutex);
1709e950267aSHenrik Ingo chain->dev = dev;
1710e950267aSHenrik Ingo v4l2_prio_init(&chain->prio);
1711e950267aSHenrik Ingo
1712e950267aSHenrik Ingo return chain;
1713e950267aSHenrik Ingo }
1714e950267aSHenrik Ingo
1715e950267aSHenrik Ingo /*
1716e950267aSHenrik Ingo * Fallback heuristic for devices that don't connect units and terminals in a
1717e950267aSHenrik Ingo * valid chain.
1718e950267aSHenrik Ingo *
1719e950267aSHenrik Ingo * Some devices have invalid baSourceID references, causing uvc_scan_chain()
1720e950267aSHenrik Ingo * to fail, but if we just take the entities we can find and put them together
1721e950267aSHenrik Ingo * in the most sensible chain we can think of, turns out they do work anyway.
1722e950267aSHenrik Ingo * Note: This heuristic assumes there is a single chain.
1723e950267aSHenrik Ingo *
1724e950267aSHenrik Ingo * At the time of writing, devices known to have such a broken chain are
1725e950267aSHenrik Ingo * - Acer Integrated Camera (5986:055a)
1726e950267aSHenrik Ingo * - Realtek rtl157a7 (0bda:57a7)
1727e950267aSHenrik Ingo */
uvc_scan_fallback(struct uvc_device * dev)1728e950267aSHenrik Ingo static int uvc_scan_fallback(struct uvc_device *dev)
1729e950267aSHenrik Ingo {
1730e950267aSHenrik Ingo struct uvc_video_chain *chain;
1731e950267aSHenrik Ingo struct uvc_entity *iterm = NULL;
1732e950267aSHenrik Ingo struct uvc_entity *oterm = NULL;
1733e950267aSHenrik Ingo struct uvc_entity *entity;
1734e950267aSHenrik Ingo struct uvc_entity *prev;
1735e950267aSHenrik Ingo
1736e950267aSHenrik Ingo /*
1737e950267aSHenrik Ingo * Start by locating the input and output terminals. We only support
1738e950267aSHenrik Ingo * devices with exactly one of each for now.
1739e950267aSHenrik Ingo */
1740e950267aSHenrik Ingo list_for_each_entry(entity, &dev->entities, list) {
1741e950267aSHenrik Ingo if (UVC_ENTITY_IS_ITERM(entity)) {
1742e950267aSHenrik Ingo if (iterm)
1743e950267aSHenrik Ingo return -EINVAL;
1744e950267aSHenrik Ingo iterm = entity;
1745e950267aSHenrik Ingo }
1746e950267aSHenrik Ingo
1747e950267aSHenrik Ingo if (UVC_ENTITY_IS_OTERM(entity)) {
1748e950267aSHenrik Ingo if (oterm)
1749e950267aSHenrik Ingo return -EINVAL;
1750e950267aSHenrik Ingo oterm = entity;
1751e950267aSHenrik Ingo }
1752e950267aSHenrik Ingo }
1753e950267aSHenrik Ingo
1754e950267aSHenrik Ingo if (iterm == NULL || oterm == NULL)
1755e950267aSHenrik Ingo return -EINVAL;
1756e950267aSHenrik Ingo
1757e950267aSHenrik Ingo /* Allocate the chain and fill it. */
1758e950267aSHenrik Ingo chain = uvc_alloc_chain(dev);
1759e950267aSHenrik Ingo if (chain == NULL)
1760e950267aSHenrik Ingo return -ENOMEM;
1761e950267aSHenrik Ingo
1762e950267aSHenrik Ingo if (uvc_scan_chain_entity(chain, oterm) < 0)
1763e950267aSHenrik Ingo goto error;
1764e950267aSHenrik Ingo
1765e950267aSHenrik Ingo prev = oterm;
1766e950267aSHenrik Ingo
1767e950267aSHenrik Ingo /*
1768e950267aSHenrik Ingo * Add all Processing and Extension Units with two pads. The order
1769e950267aSHenrik Ingo * doesn't matter much, use reverse list traversal to connect units in
1770e950267aSHenrik Ingo * UVC descriptor order as we build the chain from output to input. This
1771e950267aSHenrik Ingo * leads to units appearing in the order meant by the manufacturer for
1772e950267aSHenrik Ingo * the cameras known to require this heuristic.
1773e950267aSHenrik Ingo */
1774e950267aSHenrik Ingo list_for_each_entry_reverse(entity, &dev->entities, list) {
1775e950267aSHenrik Ingo if (entity->type != UVC_VC_PROCESSING_UNIT &&
1776e950267aSHenrik Ingo entity->type != UVC_VC_EXTENSION_UNIT)
1777e950267aSHenrik Ingo continue;
1778e950267aSHenrik Ingo
1779e950267aSHenrik Ingo if (entity->num_pads != 2)
1780e950267aSHenrik Ingo continue;
1781e950267aSHenrik Ingo
1782e950267aSHenrik Ingo if (uvc_scan_chain_entity(chain, entity) < 0)
1783e950267aSHenrik Ingo goto error;
1784e950267aSHenrik Ingo
1785e950267aSHenrik Ingo prev->baSourceID[0] = entity->id;
1786e950267aSHenrik Ingo prev = entity;
1787e950267aSHenrik Ingo }
1788e950267aSHenrik Ingo
1789e950267aSHenrik Ingo if (uvc_scan_chain_entity(chain, iterm) < 0)
1790e950267aSHenrik Ingo goto error;
1791e950267aSHenrik Ingo
1792e950267aSHenrik Ingo prev->baSourceID[0] = iterm->id;
1793e950267aSHenrik Ingo
1794e950267aSHenrik Ingo list_add_tail(&chain->list, &dev->chains);
1795e950267aSHenrik Ingo
17969e56380aSJoe Perches uvc_dbg(dev, PROBE, "Found a video chain by fallback heuristic (%s)\n",
1797e950267aSHenrik Ingo uvc_print_chain(chain));
1798e950267aSHenrik Ingo
1799e950267aSHenrik Ingo return 0;
1800e950267aSHenrik Ingo
1801e950267aSHenrik Ingo error:
1802e950267aSHenrik Ingo kfree(chain);
1803e950267aSHenrik Ingo return -EINVAL;
1804e950267aSHenrik Ingo }
1805e950267aSHenrik Ingo
18060c0d06caSMauro Carvalho Chehab /*
18070c0d06caSMauro Carvalho Chehab * Scan the device for video chains and register video devices.
18080c0d06caSMauro Carvalho Chehab *
18090c0d06caSMauro Carvalho Chehab * Chains are scanned starting at their output terminals and walked backwards.
18100c0d06caSMauro Carvalho Chehab */
uvc_scan_device(struct uvc_device * dev)18110c0d06caSMauro Carvalho Chehab static int uvc_scan_device(struct uvc_device *dev)
18120c0d06caSMauro Carvalho Chehab {
18130c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain;
18140c0d06caSMauro Carvalho Chehab struct uvc_entity *term;
18150c0d06caSMauro Carvalho Chehab
18160c0d06caSMauro Carvalho Chehab list_for_each_entry(term, &dev->entities, list) {
18170c0d06caSMauro Carvalho Chehab if (!UVC_ENTITY_IS_OTERM(term))
18180c0d06caSMauro Carvalho Chehab continue;
18190c0d06caSMauro Carvalho Chehab
1820699b9a86SLaurent Pinchart /*
1821699b9a86SLaurent Pinchart * If the terminal is already included in a chain, skip it.
18220c0d06caSMauro Carvalho Chehab * This can happen for chains that have multiple output
18230c0d06caSMauro Carvalho Chehab * terminals, where all output terminals beside the first one
18240c0d06caSMauro Carvalho Chehab * will be inserted in the chain in forward scans.
18250c0d06caSMauro Carvalho Chehab */
18260c0d06caSMauro Carvalho Chehab if (term->chain.next || term->chain.prev)
18270c0d06caSMauro Carvalho Chehab continue;
18280c0d06caSMauro Carvalho Chehab
1829e950267aSHenrik Ingo chain = uvc_alloc_chain(dev);
18300c0d06caSMauro Carvalho Chehab if (chain == NULL)
18310c0d06caSMauro Carvalho Chehab return -ENOMEM;
18320c0d06caSMauro Carvalho Chehab
18338be8ec6eSLaurent Pinchart term->flags |= UVC_ENTITY_FLAG_DEFAULT;
18348be8ec6eSLaurent Pinchart
18350c0d06caSMauro Carvalho Chehab if (uvc_scan_chain(chain, term) < 0) {
18360c0d06caSMauro Carvalho Chehab kfree(chain);
18370c0d06caSMauro Carvalho Chehab continue;
18380c0d06caSMauro Carvalho Chehab }
18390c0d06caSMauro Carvalho Chehab
18409e56380aSJoe Perches uvc_dbg(dev, PROBE, "Found a valid video chain (%s)\n",
18410c0d06caSMauro Carvalho Chehab uvc_print_chain(chain));
18420c0d06caSMauro Carvalho Chehab
18430c0d06caSMauro Carvalho Chehab list_add_tail(&chain->list, &dev->chains);
18440c0d06caSMauro Carvalho Chehab }
18450c0d06caSMauro Carvalho Chehab
1846e950267aSHenrik Ingo if (list_empty(&dev->chains))
1847e950267aSHenrik Ingo uvc_scan_fallback(dev);
1848e950267aSHenrik Ingo
18490c0d06caSMauro Carvalho Chehab if (list_empty(&dev->chains)) {
185069df0954SRicardo Ribalda dev_info(&dev->udev->dev, "No valid video chain found.\n");
18510c0d06caSMauro Carvalho Chehab return -1;
18520c0d06caSMauro Carvalho Chehab }
18530c0d06caSMauro Carvalho Chehab
18542886477fSRicardo Ribalda /* Add GPIO entity to the first chain. */
18552886477fSRicardo Ribalda if (dev->gpio_unit) {
18562886477fSRicardo Ribalda chain = list_first_entry(&dev->chains,
18572886477fSRicardo Ribalda struct uvc_video_chain, list);
18582886477fSRicardo Ribalda list_add_tail(&dev->gpio_unit->chain, &chain->entities);
18592886477fSRicardo Ribalda }
18602886477fSRicardo Ribalda
18610c0d06caSMauro Carvalho Chehab return 0;
18620c0d06caSMauro Carvalho Chehab }
18630c0d06caSMauro Carvalho Chehab
18640c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
18650c0d06caSMauro Carvalho Chehab * Video device registration and unregistration
18660c0d06caSMauro Carvalho Chehab */
18670c0d06caSMauro Carvalho Chehab
18680c0d06caSMauro Carvalho Chehab /*
18690c0d06caSMauro Carvalho Chehab * Delete the UVC device.
18700c0d06caSMauro Carvalho Chehab *
18710c0d06caSMauro Carvalho Chehab * Called by the kernel when the last reference to the uvc_device structure
18720c0d06caSMauro Carvalho Chehab * is released.
18730c0d06caSMauro Carvalho Chehab *
18740c0d06caSMauro Carvalho Chehab * As this function is called after or during disconnect(), all URBs have
1875ece41454SKieran Bingham * already been cancelled by the USB core. There is no need to kill the
18760c0d06caSMauro Carvalho Chehab * interrupt URB manually.
18770c0d06caSMauro Carvalho Chehab */
uvc_delete(struct kref * kref)18789d15cd95SGuennadi Liakhovetski static void uvc_delete(struct kref *kref)
18790c0d06caSMauro Carvalho Chehab {
18809d15cd95SGuennadi Liakhovetski struct uvc_device *dev = container_of(kref, struct uvc_device, ref);
18810c0d06caSMauro Carvalho Chehab struct list_head *p, *n;
18820c0d06caSMauro Carvalho Chehab
18830c0d06caSMauro Carvalho Chehab uvc_status_cleanup(dev);
18840c0d06caSMauro Carvalho Chehab uvc_ctrl_cleanup_device(dev);
18850c0d06caSMauro Carvalho Chehab
18862228d80dSTakashi Iwai usb_put_intf(dev->intf);
18872228d80dSTakashi Iwai usb_put_dev(dev->udev);
18882228d80dSTakashi Iwai
18890c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER
18909832e155SJavier Martinez Canillas media_device_cleanup(&dev->mdev);
18910c0d06caSMauro Carvalho Chehab #endif
18920c0d06caSMauro Carvalho Chehab
18930c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->chains) {
18940c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain;
18952a8c1952SPedro Guilherme Siqueira Moreira
18960c0d06caSMauro Carvalho Chehab chain = list_entry(p, struct uvc_video_chain, list);
18970c0d06caSMauro Carvalho Chehab kfree(chain);
18980c0d06caSMauro Carvalho Chehab }
18990c0d06caSMauro Carvalho Chehab
19000c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->entities) {
19010c0d06caSMauro Carvalho Chehab struct uvc_entity *entity;
19022a8c1952SPedro Guilherme Siqueira Moreira
19030c0d06caSMauro Carvalho Chehab entity = list_entry(p, struct uvc_entity, list);
19040c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER
19050c0d06caSMauro Carvalho Chehab uvc_mc_cleanup_entity(entity);
19060c0d06caSMauro Carvalho Chehab #endif
19070c0d06caSMauro Carvalho Chehab kfree(entity);
19080c0d06caSMauro Carvalho Chehab }
19090c0d06caSMauro Carvalho Chehab
19100c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->streams) {
19110c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming;
19122a8c1952SPedro Guilherme Siqueira Moreira
19130c0d06caSMauro Carvalho Chehab streaming = list_entry(p, struct uvc_streaming, list);
19140c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver,
19150c0d06caSMauro Carvalho Chehab streaming->intf);
1916ece41454SKieran Bingham uvc_stream_delete(streaming);
19170c0d06caSMauro Carvalho Chehab }
19180c0d06caSMauro Carvalho Chehab
19190c0d06caSMauro Carvalho Chehab kfree(dev);
19200c0d06caSMauro Carvalho Chehab }
19210c0d06caSMauro Carvalho Chehab
uvc_release(struct video_device * vdev)19220c0d06caSMauro Carvalho Chehab static void uvc_release(struct video_device *vdev)
19230c0d06caSMauro Carvalho Chehab {
19240c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream = video_get_drvdata(vdev);
19250c0d06caSMauro Carvalho Chehab struct uvc_device *dev = stream->dev;
19260c0d06caSMauro Carvalho Chehab
19279d15cd95SGuennadi Liakhovetski kref_put(&dev->ref, uvc_delete);
19280c0d06caSMauro Carvalho Chehab }
19290c0d06caSMauro Carvalho Chehab
19300c0d06caSMauro Carvalho Chehab /*
19310c0d06caSMauro Carvalho Chehab * Unregister the video devices.
19320c0d06caSMauro Carvalho Chehab */
uvc_unregister_video(struct uvc_device * dev)19330c0d06caSMauro Carvalho Chehab static void uvc_unregister_video(struct uvc_device *dev)
19340c0d06caSMauro Carvalho Chehab {
19350c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream;
19360c0d06caSMauro Carvalho Chehab
19370c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) {
1938f83f6a6eSRicardo Ribalda /* Nothing to do here, continue. */
1939d8da7513SHans Verkuil if (!video_is_registered(&stream->vdev))
19400c0d06caSMauro Carvalho Chehab continue;
19410c0d06caSMauro Carvalho Chehab
1942f83f6a6eSRicardo Ribalda /*
1943f83f6a6eSRicardo Ribalda * For stream->vdev we follow the same logic as:
1944f83f6a6eSRicardo Ribalda * vb2_video_unregister_device().
1945f83f6a6eSRicardo Ribalda */
1946f83f6a6eSRicardo Ribalda
1947f83f6a6eSRicardo Ribalda /* 1. Take a reference to vdev */
1948f83f6a6eSRicardo Ribalda get_device(&stream->vdev.dev);
1949f83f6a6eSRicardo Ribalda
1950f83f6a6eSRicardo Ribalda /* 2. Ensure that no new ioctls can be called. */
1951d8da7513SHans Verkuil video_unregister_device(&stream->vdev);
1952f83f6a6eSRicardo Ribalda
1953f83f6a6eSRicardo Ribalda /* 3. Wait for old ioctls to finish. */
1954f83f6a6eSRicardo Ribalda mutex_lock(&stream->mutex);
1955f83f6a6eSRicardo Ribalda
1956f83f6a6eSRicardo Ribalda /* 4. Stop streaming. */
1957f83f6a6eSRicardo Ribalda uvc_queue_release(&stream->queue);
1958f83f6a6eSRicardo Ribalda
1959f83f6a6eSRicardo Ribalda mutex_unlock(&stream->mutex);
1960f83f6a6eSRicardo Ribalda
1961f83f6a6eSRicardo Ribalda put_device(&stream->vdev.dev);
1962f83f6a6eSRicardo Ribalda
1963f83f6a6eSRicardo Ribalda /*
1964f83f6a6eSRicardo Ribalda * For stream->meta.vdev we can directly call:
1965f83f6a6eSRicardo Ribalda * vb2_video_unregister_device().
1966f83f6a6eSRicardo Ribalda */
1967f83f6a6eSRicardo Ribalda vb2_video_unregister_device(&stream->meta.vdev);
1968f83f6a6eSRicardo Ribalda
1969f83f6a6eSRicardo Ribalda /*
1970f83f6a6eSRicardo Ribalda * Now both vdevs are not streaming and all the ioctls will
1971f83f6a6eSRicardo Ribalda * return -ENODEV.
1972f83f6a6eSRicardo Ribalda */
19730c0d06caSMauro Carvalho Chehab
19740c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup_stream(stream);
19750c0d06caSMauro Carvalho Chehab }
197610e1fdb9SDaniel Axtens
197710e1fdb9SDaniel Axtens uvc_status_unregister(dev);
197810e1fdb9SDaniel Axtens
197910e1fdb9SDaniel Axtens if (dev->vdev.dev)
198010e1fdb9SDaniel Axtens v4l2_device_unregister(&dev->vdev);
198110e1fdb9SDaniel Axtens #ifdef CONFIG_MEDIA_CONTROLLER
198210e1fdb9SDaniel Axtens if (media_devnode_is_registered(dev->mdev.devnode))
198310e1fdb9SDaniel Axtens media_device_unregister(&dev->mdev);
198410e1fdb9SDaniel Axtens #endif
19850c0d06caSMauro Carvalho Chehab }
19860c0d06caSMauro Carvalho Chehab
uvc_register_video_device(struct uvc_device * dev,struct uvc_streaming * stream,struct video_device * vdev,struct uvc_video_queue * queue,enum v4l2_buf_type type,const struct v4l2_file_operations * fops,const struct v4l2_ioctl_ops * ioctl_ops)198731a96f4cSLaurent Pinchart int uvc_register_video_device(struct uvc_device *dev,
198831a96f4cSLaurent Pinchart struct uvc_streaming *stream,
198931a96f4cSLaurent Pinchart struct video_device *vdev,
199031a96f4cSLaurent Pinchart struct uvc_video_queue *queue,
199131a96f4cSLaurent Pinchart enum v4l2_buf_type type,
199231a96f4cSLaurent Pinchart const struct v4l2_file_operations *fops,
199331a96f4cSLaurent Pinchart const struct v4l2_ioctl_ops *ioctl_ops)
19940c0d06caSMauro Carvalho Chehab {
19950c0d06caSMauro Carvalho Chehab int ret;
19960c0d06caSMauro Carvalho Chehab
1997b83bba24SLaurent Pinchart /* Initialize the video buffers queue. */
199831a96f4cSLaurent Pinchart ret = uvc_queue_init(queue, type, !uvc_no_drop_param);
1999b83bba24SLaurent Pinchart if (ret)
2000b83bba24SLaurent Pinchart return ret;
2001b83bba24SLaurent Pinchart
20020c0d06caSMauro Carvalho Chehab /* Register the device with V4L. */
20030c0d06caSMauro Carvalho Chehab
200431a96f4cSLaurent Pinchart /*
200531a96f4cSLaurent Pinchart * We already hold a reference to dev->udev. The video device will be
20060c0d06caSMauro Carvalho Chehab * unregistered before the reference is released, so we don't need to
20070c0d06caSMauro Carvalho Chehab * get another one.
20080c0d06caSMauro Carvalho Chehab */
20090c0d06caSMauro Carvalho Chehab vdev->v4l2_dev = &dev->vdev;
201031a96f4cSLaurent Pinchart vdev->fops = fops;
201131a96f4cSLaurent Pinchart vdev->ioctl_ops = ioctl_ops;
20120c0d06caSMauro Carvalho Chehab vdev->release = uvc_release;
20130550513cSLaurent Pinchart vdev->prio = &stream->chain->prio;
201431a96f4cSLaurent Pinchart if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
2015954f340fSHans Verkuil vdev->vfl_dir = VFL_DIR_TX;
201631a96f4cSLaurent Pinchart else
201731a96f4cSLaurent Pinchart vdev->vfl_dir = VFL_DIR_RX;
201894c53e26SLaurent Pinchart
201994c53e26SLaurent Pinchart switch (type) {
202094c53e26SLaurent Pinchart case V4L2_BUF_TYPE_VIDEO_CAPTURE:
202194c53e26SLaurent Pinchart default:
202294c53e26SLaurent Pinchart vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
202394c53e26SLaurent Pinchart break;
202494c53e26SLaurent Pinchart case V4L2_BUF_TYPE_VIDEO_OUTPUT:
202594c53e26SLaurent Pinchart vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
202694c53e26SLaurent Pinchart break;
2027088ead25SGuennadi Liakhovetski case V4L2_BUF_TYPE_META_CAPTURE:
2028088ead25SGuennadi Liakhovetski vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
2029088ead25SGuennadi Liakhovetski break;
203094c53e26SLaurent Pinchart }
203194c53e26SLaurent Pinchart
2032f66dcb32SRicardo Ribalda strscpy(vdev->name, dev->name, sizeof(vdev->name));
20330c0d06caSMauro Carvalho Chehab
203431a96f4cSLaurent Pinchart /*
203531a96f4cSLaurent Pinchart * Set the driver data before calling video_register_device, otherwise
203631a96f4cSLaurent Pinchart * the file open() handler might race us.
20370c0d06caSMauro Carvalho Chehab */
20380c0d06caSMauro Carvalho Chehab video_set_drvdata(vdev, stream);
20390c0d06caSMauro Carvalho Chehab
20407fbbbc78SHans Verkuil ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
20410c0d06caSMauro Carvalho Chehab if (ret < 0) {
204269df0954SRicardo Ribalda dev_err(&stream->intf->dev,
204369df0954SRicardo Ribalda "Failed to register %s device (%d).\n",
204431a96f4cSLaurent Pinchart v4l2_type_names[type], ret);
204531a96f4cSLaurent Pinchart return ret;
204631a96f4cSLaurent Pinchart }
204731a96f4cSLaurent Pinchart
204831a96f4cSLaurent Pinchart kref_get(&dev->ref);
204931a96f4cSLaurent Pinchart return 0;
205031a96f4cSLaurent Pinchart }
205131a96f4cSLaurent Pinchart
uvc_register_video(struct uvc_device * dev,struct uvc_streaming * stream)205231a96f4cSLaurent Pinchart static int uvc_register_video(struct uvc_device *dev,
205331a96f4cSLaurent Pinchart struct uvc_streaming *stream)
205431a96f4cSLaurent Pinchart {
205531a96f4cSLaurent Pinchart int ret;
205631a96f4cSLaurent Pinchart
205731a96f4cSLaurent Pinchart /* Initialize the streaming interface with default parameters. */
205831a96f4cSLaurent Pinchart ret = uvc_video_init(stream);
205931a96f4cSLaurent Pinchart if (ret < 0) {
206069df0954SRicardo Ribalda dev_err(&stream->intf->dev,
206169df0954SRicardo Ribalda "Failed to initialize the device (%d).\n", ret);
20620c0d06caSMauro Carvalho Chehab return ret;
20630c0d06caSMauro Carvalho Chehab }
20640c0d06caSMauro Carvalho Chehab
2065f887e99aSLaurent Pinchart if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
2066088ead25SGuennadi Liakhovetski stream->chain->caps |= V4L2_CAP_VIDEO_CAPTURE
2067088ead25SGuennadi Liakhovetski | V4L2_CAP_META_CAPTURE;
2068f887e99aSLaurent Pinchart else
2069f887e99aSLaurent Pinchart stream->chain->caps |= V4L2_CAP_VIDEO_OUTPUT;
2070f887e99aSLaurent Pinchart
207131a96f4cSLaurent Pinchart uvc_debugfs_init_stream(stream);
207231a96f4cSLaurent Pinchart
207331a96f4cSLaurent Pinchart /* Register the device with V4L. */
207431a96f4cSLaurent Pinchart return uvc_register_video_device(dev, stream, &stream->vdev,
207531a96f4cSLaurent Pinchart &stream->queue, stream->type,
207631a96f4cSLaurent Pinchart &uvc_fops, &uvc_ioctl_ops);
20770c0d06caSMauro Carvalho Chehab }
20780c0d06caSMauro Carvalho Chehab
20790c0d06caSMauro Carvalho Chehab /*
20800c0d06caSMauro Carvalho Chehab * Register all video devices in all chains.
20810c0d06caSMauro Carvalho Chehab */
uvc_register_terms(struct uvc_device * dev,struct uvc_video_chain * chain)20820c0d06caSMauro Carvalho Chehab static int uvc_register_terms(struct uvc_device *dev,
20830c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain)
20840c0d06caSMauro Carvalho Chehab {
20850c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream;
20860c0d06caSMauro Carvalho Chehab struct uvc_entity *term;
20870c0d06caSMauro Carvalho Chehab int ret;
20880c0d06caSMauro Carvalho Chehab
20890c0d06caSMauro Carvalho Chehab list_for_each_entry(term, &chain->entities, chain) {
20900c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
20910c0d06caSMauro Carvalho Chehab continue;
20920c0d06caSMauro Carvalho Chehab
20930c0d06caSMauro Carvalho Chehab stream = uvc_stream_by_id(dev, term->id);
20940c0d06caSMauro Carvalho Chehab if (stream == NULL) {
209569df0954SRicardo Ribalda dev_info(&dev->udev->dev,
209669df0954SRicardo Ribalda "No streaming interface found for terminal %u.",
209769df0954SRicardo Ribalda term->id);
20980c0d06caSMauro Carvalho Chehab continue;
20990c0d06caSMauro Carvalho Chehab }
21000c0d06caSMauro Carvalho Chehab
21010c0d06caSMauro Carvalho Chehab stream->chain = chain;
21020c0d06caSMauro Carvalho Chehab ret = uvc_register_video(dev, stream);
21030c0d06caSMauro Carvalho Chehab if (ret < 0)
21040c0d06caSMauro Carvalho Chehab return ret;
21050c0d06caSMauro Carvalho Chehab
2106699b9a86SLaurent Pinchart /*
2107699b9a86SLaurent Pinchart * Register a metadata node, but ignore a possible failure,
2108088ead25SGuennadi Liakhovetski * complete registration of video nodes anyway.
2109088ead25SGuennadi Liakhovetski */
2110088ead25SGuennadi Liakhovetski uvc_meta_register(stream);
2111088ead25SGuennadi Liakhovetski
2112d8da7513SHans Verkuil term->vdev = &stream->vdev;
21130c0d06caSMauro Carvalho Chehab }
21140c0d06caSMauro Carvalho Chehab
21150c0d06caSMauro Carvalho Chehab return 0;
21160c0d06caSMauro Carvalho Chehab }
21170c0d06caSMauro Carvalho Chehab
uvc_register_chains(struct uvc_device * dev)21180c0d06caSMauro Carvalho Chehab static int uvc_register_chains(struct uvc_device *dev)
21190c0d06caSMauro Carvalho Chehab {
21200c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain;
21210c0d06caSMauro Carvalho Chehab int ret;
21220c0d06caSMauro Carvalho Chehab
21230c0d06caSMauro Carvalho Chehab list_for_each_entry(chain, &dev->chains, list) {
21240c0d06caSMauro Carvalho Chehab ret = uvc_register_terms(dev, chain);
21250c0d06caSMauro Carvalho Chehab if (ret < 0)
21260c0d06caSMauro Carvalho Chehab return ret;
21270c0d06caSMauro Carvalho Chehab
21280c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER
21290c0d06caSMauro Carvalho Chehab ret = uvc_mc_register_entities(chain);
21306689df06SColin Ian King if (ret < 0)
213169df0954SRicardo Ribalda dev_info(&dev->udev->dev,
21326689df06SColin Ian King "Failed to register entities (%d).\n", ret);
21330c0d06caSMauro Carvalho Chehab #endif
21340c0d06caSMauro Carvalho Chehab }
21350c0d06caSMauro Carvalho Chehab
21360c0d06caSMauro Carvalho Chehab return 0;
21370c0d06caSMauro Carvalho Chehab }
21380c0d06caSMauro Carvalho Chehab
21390c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
21400c0d06caSMauro Carvalho Chehab * USB probe, disconnect, suspend and resume
21410c0d06caSMauro Carvalho Chehab */
21420c0d06caSMauro Carvalho Chehab
21433a03284dSLaurent Pinchart static const struct uvc_device_info uvc_quirk_none = { 0 };
21443bc85817SGuennadi Liakhovetski
uvc_probe(struct usb_interface * intf,const struct usb_device_id * id)21450c0d06caSMauro Carvalho Chehab static int uvc_probe(struct usb_interface *intf,
21460c0d06caSMauro Carvalho Chehab const struct usb_device_id *id)
21470c0d06caSMauro Carvalho Chehab {
21480c0d06caSMauro Carvalho Chehab struct usb_device *udev = interface_to_usbdev(intf);
21490c0d06caSMauro Carvalho Chehab struct uvc_device *dev;
21503bc85817SGuennadi Liakhovetski const struct uvc_device_info *info =
21513bc85817SGuennadi Liakhovetski (const struct uvc_device_info *)id->driver_info;
2152e7b09f18SPeter Boström int function;
21530c0d06caSMauro Carvalho Chehab int ret;
21540c0d06caSMauro Carvalho Chehab
21550c0d06caSMauro Carvalho Chehab /* Allocate memory for the device and initialize it. */
2156f14d4988SLaurent Pinchart dev = kzalloc(sizeof(*dev), GFP_KERNEL);
2157f14d4988SLaurent Pinchart if (dev == NULL)
21580c0d06caSMauro Carvalho Chehab return -ENOMEM;
21590c0d06caSMauro Carvalho Chehab
21600c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->entities);
21610c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->chains);
21620c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->streams);
21639d15cd95SGuennadi Liakhovetski kref_init(&dev->ref);
21640c0d06caSMauro Carvalho Chehab atomic_set(&dev->nmappings, 0);
216517706f56SLaurent Pinchart mutex_init(&dev->lock);
21660c0d06caSMauro Carvalho Chehab
21670c0d06caSMauro Carvalho Chehab dev->udev = usb_get_dev(udev);
21680c0d06caSMauro Carvalho Chehab dev->intf = usb_get_intf(intf);
21690c0d06caSMauro Carvalho Chehab dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
21703a03284dSLaurent Pinchart dev->info = info ? info : &uvc_quirk_none;
21713a03284dSLaurent Pinchart dev->quirks = uvc_quirks_param == -1
21723a03284dSLaurent Pinchart ? dev->info->quirks : uvc_quirks_param;
21730c0d06caSMauro Carvalho Chehab
2174ed4c5fa4SRicardo Ribalda if (id->idVendor && id->idProduct)
21759e56380aSJoe Perches uvc_dbg(dev, PROBE, "Probing known UVC device %s (%04x:%04x)\n",
2176ed4c5fa4SRicardo Ribalda udev->devpath, id->idVendor, id->idProduct);
2177ed4c5fa4SRicardo Ribalda else
21789e56380aSJoe Perches uvc_dbg(dev, PROBE, "Probing generic UVC device %s\n",
21799e56380aSJoe Perches udev->devpath);
2180ed4c5fa4SRicardo Ribalda
21810c0d06caSMauro Carvalho Chehab if (udev->product != NULL)
2182c0decac1SMauro Carvalho Chehab strscpy(dev->name, udev->product, sizeof(dev->name));
21830c0d06caSMauro Carvalho Chehab else
2184f14d4988SLaurent Pinchart snprintf(dev->name, sizeof(dev->name),
21850c0d06caSMauro Carvalho Chehab "UVC Camera (%04x:%04x)",
21860c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idVendor),
21870c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idProduct));
21880c0d06caSMauro Carvalho Chehab
2189e7b09f18SPeter Boström /*
2190e7b09f18SPeter Boström * Add iFunction or iInterface to names when available as additional
2191e7b09f18SPeter Boström * distinguishers between interfaces. iFunction is prioritized over
2192e7b09f18SPeter Boström * iInterface which matches Windows behavior at the point of writing.
2193e7b09f18SPeter Boström */
2194e7b09f18SPeter Boström if (intf->intf_assoc && intf->intf_assoc->iFunction != 0)
2195e7b09f18SPeter Boström function = intf->intf_assoc->iFunction;
2196e7b09f18SPeter Boström else
2197e7b09f18SPeter Boström function = intf->cur_altsetting->desc.iInterface;
2198e7b09f18SPeter Boström if (function != 0) {
2199e7b09f18SPeter Boström size_t len;
2200e7b09f18SPeter Boström
2201e7b09f18SPeter Boström strlcat(dev->name, ": ", sizeof(dev->name));
2202e7b09f18SPeter Boström len = strlen(dev->name);
2203e7b09f18SPeter Boström usb_string(udev, function, dev->name + len,
2204e7b09f18SPeter Boström sizeof(dev->name) - len);
2205e7b09f18SPeter Boström }
2206e7b09f18SPeter Boström
22078c279e93SLaurent Pinchart /* Initialize the media device. */
22088c279e93SLaurent Pinchart #ifdef CONFIG_MEDIA_CONTROLLER
22098c279e93SLaurent Pinchart dev->mdev.dev = &intf->dev;
22108c279e93SLaurent Pinchart strscpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
22118c279e93SLaurent Pinchart if (udev->serial)
22128c279e93SLaurent Pinchart strscpy(dev->mdev.serial, udev->serial,
22138c279e93SLaurent Pinchart sizeof(dev->mdev.serial));
22148c279e93SLaurent Pinchart usb_make_path(udev, dev->mdev.bus_info, sizeof(dev->mdev.bus_info));
22158c279e93SLaurent Pinchart dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
22168c279e93SLaurent Pinchart media_device_init(&dev->mdev);
22178c279e93SLaurent Pinchart
22188c279e93SLaurent Pinchart dev->vdev.mdev = &dev->mdev;
22198c279e93SLaurent Pinchart #endif
22208c279e93SLaurent Pinchart
22210c0d06caSMauro Carvalho Chehab /* Parse the Video Class control descriptor. */
22220c0d06caSMauro Carvalho Chehab if (uvc_parse_control(dev) < 0) {
22239e56380aSJoe Perches uvc_dbg(dev, PROBE, "Unable to parse UVC descriptors\n");
22240c0d06caSMauro Carvalho Chehab goto error;
22250c0d06caSMauro Carvalho Chehab }
22260c0d06caSMauro Carvalho Chehab
22272886477fSRicardo Ribalda /* Parse the associated GPIOs. */
22282886477fSRicardo Ribalda if (uvc_gpio_parse(dev) < 0) {
22299e56380aSJoe Perches uvc_dbg(dev, PROBE, "Unable to parse UVC GPIOs\n");
22302886477fSRicardo Ribalda goto error;
22312886477fSRicardo Ribalda }
22322886477fSRicardo Ribalda
223369df0954SRicardo Ribalda dev_info(&dev->udev->dev, "Found UVC %u.%02x device %s (%04x:%04x)\n",
22340c0d06caSMauro Carvalho Chehab dev->uvc_version >> 8, dev->uvc_version & 0xff,
22350c0d06caSMauro Carvalho Chehab udev->product ? udev->product : "<unnamed>",
22360c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idVendor),
22370c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idProduct));
22380c0d06caSMauro Carvalho Chehab
22393a03284dSLaurent Pinchart if (dev->quirks != dev->info->quirks) {
224069df0954SRicardo Ribalda dev_info(&dev->udev->dev,
224169df0954SRicardo Ribalda "Forcing device quirks to 0x%x by module parameter for testing purpose.\n",
224269df0954SRicardo Ribalda dev->quirks);
224369df0954SRicardo Ribalda dev_info(&dev->udev->dev,
2244e3a0f556SJonathan Neuschäfer "Please report required quirks to the linux-media mailing list.\n");
22450c0d06caSMauro Carvalho Chehab }
22460c0d06caSMauro Carvalho Chehab
2247b400b6f2SLaurent Pinchart if (dev->info->uvc_version) {
2248b400b6f2SLaurent Pinchart dev->uvc_version = dev->info->uvc_version;
224969df0954SRicardo Ribalda dev_info(&dev->udev->dev, "Forcing UVC version to %u.%02x\n",
2250b400b6f2SLaurent Pinchart dev->uvc_version >> 8, dev->uvc_version & 0xff);
2251b400b6f2SLaurent Pinchart }
2252b400b6f2SLaurent Pinchart
22538c279e93SLaurent Pinchart /* Register the V4L2 device. */
22540c0d06caSMauro Carvalho Chehab if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
22550c0d06caSMauro Carvalho Chehab goto error;
22560c0d06caSMauro Carvalho Chehab
22570c0d06caSMauro Carvalho Chehab /* Scan the device for video chains. */
22580c0d06caSMauro Carvalho Chehab if (uvc_scan_device(dev) < 0)
22590c0d06caSMauro Carvalho Chehab goto error;
22600c0d06caSMauro Carvalho Chehab
2261866c6bddSRicardo Ribalda /* Initialize controls. */
2262866c6bddSRicardo Ribalda if (uvc_ctrl_init_device(dev) < 0)
2263866c6bddSRicardo Ribalda goto error;
2264866c6bddSRicardo Ribalda
22650c0d06caSMauro Carvalho Chehab /* Register video device nodes. */
22660c0d06caSMauro Carvalho Chehab if (uvc_register_chains(dev) < 0)
22670c0d06caSMauro Carvalho Chehab goto error;
22680c0d06caSMauro Carvalho Chehab
22699832e155SJavier Martinez Canillas #ifdef CONFIG_MEDIA_CONTROLLER
22709832e155SJavier Martinez Canillas /* Register the media device node */
22719832e155SJavier Martinez Canillas if (media_device_register(&dev->mdev) < 0)
22729832e155SJavier Martinez Canillas goto error;
22739832e155SJavier Martinez Canillas #endif
22740c0d06caSMauro Carvalho Chehab /* Save our data pointer in the interface data. */
22750c0d06caSMauro Carvalho Chehab usb_set_intfdata(intf, dev);
22760c0d06caSMauro Carvalho Chehab
22770c0d06caSMauro Carvalho Chehab /* Initialize the interrupt URB. */
22787b78a846SPedro Guilherme Siqueira Moreira ret = uvc_status_init(dev);
22797b78a846SPedro Guilherme Siqueira Moreira if (ret < 0) {
228069df0954SRicardo Ribalda dev_info(&dev->udev->dev,
228169df0954SRicardo Ribalda "Unable to initialize the status endpoint (%d), status interrupt will not be supported.\n",
228269df0954SRicardo Ribalda ret);
22830c0d06caSMauro Carvalho Chehab }
22840c0d06caSMauro Carvalho Chehab
22852886477fSRicardo Ribalda ret = uvc_gpio_init_irq(dev);
22862886477fSRicardo Ribalda if (ret < 0) {
22872886477fSRicardo Ribalda dev_err(&dev->udev->dev,
22882886477fSRicardo Ribalda "Unable to request privacy GPIO IRQ (%d)\n", ret);
22892886477fSRicardo Ribalda goto error;
22902886477fSRicardo Ribalda }
22912886477fSRicardo Ribalda
22924b3421c2SRicardo Ribalda if (dev->quirks & UVC_QUIRK_NO_RESET_RESUME)
22934b3421c2SRicardo Ribalda udev->quirks &= ~USB_QUIRK_RESET_RESUME;
22944b3421c2SRicardo Ribalda
2295991df394SRicardo Ribalda if (!(dev->quirks & UVC_QUIRK_DISABLE_AUTOSUSPEND))
22960c0d06caSMauro Carvalho Chehab usb_enable_autosuspend(udev);
2297991df394SRicardo Ribalda
2298991df394SRicardo Ribalda uvc_dbg(dev, PROBE, "UVC device initialized\n");
2299991df394SRicardo Ribalda
23000c0d06caSMauro Carvalho Chehab return 0;
23010c0d06caSMauro Carvalho Chehab
23020c0d06caSMauro Carvalho Chehab error:
23030c0d06caSMauro Carvalho Chehab uvc_unregister_video(dev);
2304f9ffcb0aSPhilipp Zabel kref_put(&dev->ref, uvc_delete);
23050c0d06caSMauro Carvalho Chehab return -ENODEV;
23060c0d06caSMauro Carvalho Chehab }
23070c0d06caSMauro Carvalho Chehab
uvc_disconnect(struct usb_interface * intf)23080c0d06caSMauro Carvalho Chehab static void uvc_disconnect(struct usb_interface *intf)
23090c0d06caSMauro Carvalho Chehab {
23100c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf);
23110c0d06caSMauro Carvalho Chehab
2312699b9a86SLaurent Pinchart /*
2313699b9a86SLaurent Pinchart * Set the USB interface data to NULL. This can be done outside the
23140c0d06caSMauro Carvalho Chehab * lock, as there's no other reader.
23150c0d06caSMauro Carvalho Chehab */
23160c0d06caSMauro Carvalho Chehab usb_set_intfdata(intf, NULL);
23170c0d06caSMauro Carvalho Chehab
23180c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass ==
23190c0d06caSMauro Carvalho Chehab UVC_SC_VIDEOSTREAMING)
23200c0d06caSMauro Carvalho Chehab return;
23210c0d06caSMauro Carvalho Chehab
23220c0d06caSMauro Carvalho Chehab uvc_unregister_video(dev);
2323f9ffcb0aSPhilipp Zabel kref_put(&dev->ref, uvc_delete);
23240c0d06caSMauro Carvalho Chehab }
23250c0d06caSMauro Carvalho Chehab
uvc_suspend(struct usb_interface * intf,pm_message_t message)23260c0d06caSMauro Carvalho Chehab static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
23270c0d06caSMauro Carvalho Chehab {
23280c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf);
23290c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream;
23300c0d06caSMauro Carvalho Chehab
23319e56380aSJoe Perches uvc_dbg(dev, SUSPEND, "Suspending interface %u\n",
23320c0d06caSMauro Carvalho Chehab intf->cur_altsetting->desc.bInterfaceNumber);
23330c0d06caSMauro Carvalho Chehab
23340c0d06caSMauro Carvalho Chehab /* Controls are cached on the fly so they don't need to be saved. */
23350c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass ==
233617706f56SLaurent Pinchart UVC_SC_VIDEOCONTROL) {
233717706f56SLaurent Pinchart mutex_lock(&dev->lock);
233817706f56SLaurent Pinchart if (dev->users)
233917706f56SLaurent Pinchart uvc_status_stop(dev);
234017706f56SLaurent Pinchart mutex_unlock(&dev->lock);
234117706f56SLaurent Pinchart return 0;
234217706f56SLaurent Pinchart }
23430c0d06caSMauro Carvalho Chehab
23440c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) {
23450c0d06caSMauro Carvalho Chehab if (stream->intf == intf)
23460c0d06caSMauro Carvalho Chehab return uvc_video_suspend(stream);
23470c0d06caSMauro Carvalho Chehab }
23480c0d06caSMauro Carvalho Chehab
23499e56380aSJoe Perches uvc_dbg(dev, SUSPEND,
23509e56380aSJoe Perches "Suspend: video streaming USB interface mismatch\n");
23510c0d06caSMauro Carvalho Chehab return -EINVAL;
23520c0d06caSMauro Carvalho Chehab }
23530c0d06caSMauro Carvalho Chehab
__uvc_resume(struct usb_interface * intf,int reset)23540c0d06caSMauro Carvalho Chehab static int __uvc_resume(struct usb_interface *intf, int reset)
23550c0d06caSMauro Carvalho Chehab {
23560c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf);
23570c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream;
2358b83bba24SLaurent Pinchart int ret = 0;
23590c0d06caSMauro Carvalho Chehab
23609e56380aSJoe Perches uvc_dbg(dev, SUSPEND, "Resuming interface %u\n",
23610c0d06caSMauro Carvalho Chehab intf->cur_altsetting->desc.bInterfaceNumber);
23620c0d06caSMauro Carvalho Chehab
23630c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass ==
23640c0d06caSMauro Carvalho Chehab UVC_SC_VIDEOCONTROL) {
236517706f56SLaurent Pinchart if (reset) {
236617e1319fSWilliam Manley ret = uvc_ctrl_restore_values(dev);
23670c0d06caSMauro Carvalho Chehab if (ret < 0)
23680c0d06caSMauro Carvalho Chehab return ret;
23690c0d06caSMauro Carvalho Chehab }
23700c0d06caSMauro Carvalho Chehab
237117706f56SLaurent Pinchart mutex_lock(&dev->lock);
237217706f56SLaurent Pinchart if (dev->users)
237317706f56SLaurent Pinchart ret = uvc_status_start(dev, GFP_NOIO);
237417706f56SLaurent Pinchart mutex_unlock(&dev->lock);
237517706f56SLaurent Pinchart
237617706f56SLaurent Pinchart return ret;
23770c0d06caSMauro Carvalho Chehab }
23780c0d06caSMauro Carvalho Chehab
23790c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) {
2380b83bba24SLaurent Pinchart if (stream->intf == intf) {
2381b83bba24SLaurent Pinchart ret = uvc_video_resume(stream, reset);
2382b83bba24SLaurent Pinchart if (ret < 0)
23830da4ab98SLaurent Pinchart uvc_queue_streamoff(&stream->queue,
23840da4ab98SLaurent Pinchart stream->queue.queue.type);
2385b83bba24SLaurent Pinchart return ret;
2386b83bba24SLaurent Pinchart }
23870c0d06caSMauro Carvalho Chehab }
23880c0d06caSMauro Carvalho Chehab
23899e56380aSJoe Perches uvc_dbg(dev, SUSPEND,
23909e56380aSJoe Perches "Resume: video streaming USB interface mismatch\n");
23910c0d06caSMauro Carvalho Chehab return -EINVAL;
23920c0d06caSMauro Carvalho Chehab }
23930c0d06caSMauro Carvalho Chehab
uvc_resume(struct usb_interface * intf)23940c0d06caSMauro Carvalho Chehab static int uvc_resume(struct usb_interface *intf)
23950c0d06caSMauro Carvalho Chehab {
23960c0d06caSMauro Carvalho Chehab return __uvc_resume(intf, 0);
23970c0d06caSMauro Carvalho Chehab }
23980c0d06caSMauro Carvalho Chehab
uvc_reset_resume(struct usb_interface * intf)23990c0d06caSMauro Carvalho Chehab static int uvc_reset_resume(struct usb_interface *intf)
24000c0d06caSMauro Carvalho Chehab {
24010c0d06caSMauro Carvalho Chehab return __uvc_resume(intf, 1);
24020c0d06caSMauro Carvalho Chehab }
24030c0d06caSMauro Carvalho Chehab
24040c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
24050c0d06caSMauro Carvalho Chehab * Module parameters
24060c0d06caSMauro Carvalho Chehab */
24070c0d06caSMauro Carvalho Chehab
uvc_clock_param_get(char * buffer,const struct kernel_param * kp)2408e4dca7b7SKees Cook static int uvc_clock_param_get(char *buffer, const struct kernel_param *kp)
24090c0d06caSMauro Carvalho Chehab {
24100c0d06caSMauro Carvalho Chehab if (uvc_clock_param == CLOCK_MONOTONIC)
24110c0d06caSMauro Carvalho Chehab return sprintf(buffer, "CLOCK_MONOTONIC");
24120c0d06caSMauro Carvalho Chehab else
24130c0d06caSMauro Carvalho Chehab return sprintf(buffer, "CLOCK_REALTIME");
24140c0d06caSMauro Carvalho Chehab }
24150c0d06caSMauro Carvalho Chehab
uvc_clock_param_set(const char * val,const struct kernel_param * kp)2416e4dca7b7SKees Cook static int uvc_clock_param_set(const char *val, const struct kernel_param *kp)
24170c0d06caSMauro Carvalho Chehab {
24180c0d06caSMauro Carvalho Chehab if (strncasecmp(val, "clock_", strlen("clock_")) == 0)
24190c0d06caSMauro Carvalho Chehab val += strlen("clock_");
24200c0d06caSMauro Carvalho Chehab
24210c0d06caSMauro Carvalho Chehab if (strcasecmp(val, "monotonic") == 0)
24220c0d06caSMauro Carvalho Chehab uvc_clock_param = CLOCK_MONOTONIC;
24230c0d06caSMauro Carvalho Chehab else if (strcasecmp(val, "realtime") == 0)
24240c0d06caSMauro Carvalho Chehab uvc_clock_param = CLOCK_REALTIME;
24250c0d06caSMauro Carvalho Chehab else
24260c0d06caSMauro Carvalho Chehab return -EINVAL;
24270c0d06caSMauro Carvalho Chehab
24280c0d06caSMauro Carvalho Chehab return 0;
24290c0d06caSMauro Carvalho Chehab }
24300c0d06caSMauro Carvalho Chehab
24310c0d06caSMauro Carvalho Chehab module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get,
24320ce75d5eSPedro Guilherme Siqueira Moreira &uvc_clock_param, 0644);
24330c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(clock, "Video buffers timestamp clock");
24340ce75d5eSPedro Guilherme Siqueira Moreira module_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, 0644);
24355d0fd3c8SLaurent Pinchart MODULE_PARM_DESC(hwtimestamps, "Use hardware timestamps");
24360ce75d5eSPedro Guilherme Siqueira Moreira module_param_named(nodrop, uvc_no_drop_param, uint, 0644);
24370c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
24380ce75d5eSPedro Guilherme Siqueira Moreira module_param_named(quirks, uvc_quirks_param, uint, 0644);
24390c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(quirks, "Forced device quirks");
24400ce75d5eSPedro Guilherme Siqueira Moreira module_param_named(trace, uvc_dbg_param, uint, 0644);
24410c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(trace, "Trace level bitmask");
24420ce75d5eSPedro Guilherme Siqueira Moreira module_param_named(timeout, uvc_timeout_param, uint, 0644);
24430c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
24440c0d06caSMauro Carvalho Chehab
24450c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
24460c0d06caSMauro Carvalho Chehab * Driver initialization and cleanup
24470c0d06caSMauro Carvalho Chehab */
24480c0d06caSMauro Carvalho Chehab
244938207560SRicardo Ribalda static const struct uvc_device_info uvc_ctrl_power_line_limited = {
245038207560SRicardo Ribalda .mappings = (const struct uvc_control_mapping *[]) {
245138207560SRicardo Ribalda &uvc_ctrl_power_line_mapping_limited,
245238207560SRicardo Ribalda NULL, /* Sentinel */
245338207560SRicardo Ribalda },
245438207560SRicardo Ribalda };
245538207560SRicardo Ribalda
2456a7c28150SRicardo Ribalda static const struct uvc_device_info uvc_ctrl_power_line_uvc11 = {
2457a7c28150SRicardo Ribalda .mappings = (const struct uvc_control_mapping *[]) {
2458a7c28150SRicardo Ribalda &uvc_ctrl_power_line_mapping_uvc11,
2459a7c28150SRicardo Ribalda NULL, /* Sentinel */
2460a7c28150SRicardo Ribalda },
2461a7c28150SRicardo Ribalda };
2462a7c28150SRicardo Ribalda
24633bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_probe_minmax = {
24643bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_PROBE_MINMAX,
24653bc85817SGuennadi Liakhovetski };
24663bc85817SGuennadi Liakhovetski
24673bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_fix_bandwidth = {
24683bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_FIX_BANDWIDTH,
24693bc85817SGuennadi Liakhovetski };
24703bc85817SGuennadi Liakhovetski
24713bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_probe_def = {
24723bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_PROBE_DEF,
24733bc85817SGuennadi Liakhovetski };
24743bc85817SGuennadi Liakhovetski
24753bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_stream_no_fid = {
24763bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_STREAM_NO_FID,
24773bc85817SGuennadi Liakhovetski };
24783bc85817SGuennadi Liakhovetski
24793bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_force_y8 = {
24803bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_FORCE_Y8,
24813bc85817SGuennadi Liakhovetski };
24823bc85817SGuennadi Liakhovetski
248388d8034cSGuennadi Liakhovetski #define UVC_INFO_QUIRK(q) (kernel_ulong_t)&(struct uvc_device_info){.quirks = q}
24846ea0d588SGuennadi Liakhovetski #define UVC_INFO_META(m) (kernel_ulong_t)&(struct uvc_device_info) \
24856ea0d588SGuennadi Liakhovetski {.meta_format = m}
24863bc85817SGuennadi Liakhovetski
24870c0d06caSMauro Carvalho Chehab /*
24880c0d06caSMauro Carvalho Chehab * The Logitech cameras listed below have their interface class set to
24890c0d06caSMauro Carvalho Chehab * VENDOR_SPEC because they don't announce themselves as UVC devices, even
24900c0d06caSMauro Carvalho Chehab * though they are compliant.
249131f9b4a2SDavid Given *
249231f9b4a2SDavid Given * Sort these by vendor/product ID.
24930c0d06caSMauro Carvalho Chehab */
24947fb2e072SArvind Yadav static const struct usb_device_id uvc_ids[] = {
249538207560SRicardo Ribalda /* Quanta USB2.0 HD UVC Webcam */
249638207560SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
249738207560SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
249838207560SRicardo Ribalda .idVendor = 0x0408,
249938207560SRicardo Ribalda .idProduct = 0x3090,
250038207560SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
250138207560SRicardo Ribalda .bInterfaceSubClass = 1,
250238207560SRicardo Ribalda .bInterfaceProtocol = 0,
250338207560SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
250495f03d97SRicardo Ribalda /* Quanta USB2.0 HD UVC Webcam */
250595f03d97SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
250695f03d97SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
250795f03d97SRicardo Ribalda .idVendor = 0x0408,
250895f03d97SRicardo Ribalda .idProduct = 0x4030,
250995f03d97SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
251095f03d97SRicardo Ribalda .bInterfaceSubClass = 1,
251195f03d97SRicardo Ribalda .bInterfaceProtocol = 0,
251295f03d97SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
251395f03d97SRicardo Ribalda /* Quanta USB2.0 HD UVC Webcam */
251495f03d97SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
251595f03d97SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
251695f03d97SRicardo Ribalda .idVendor = 0x0408,
251795f03d97SRicardo Ribalda .idProduct = 0x4034,
251895f03d97SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
251995f03d97SRicardo Ribalda .bInterfaceSubClass = 1,
2520150f7b11SRicardo Ribalda .bInterfaceProtocol = UVC_PC_PROTOCOL_15,
252195f03d97SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
25229471b8f8SLaurent Pinchart /* Quanta ACER HD User Facing */
25239471b8f8SLaurent Pinchart { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
25249471b8f8SLaurent Pinchart | USB_DEVICE_ID_MATCH_INT_INFO,
25259471b8f8SLaurent Pinchart .idVendor = 0x0408,
2526*ed01e57aSRicardo Ribalda .idProduct = 0x4033,
2527*ed01e57aSRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
2528*ed01e57aSRicardo Ribalda .bInterfaceSubClass = 1,
2529*ed01e57aSRicardo Ribalda .bInterfaceProtocol = UVC_PC_PROTOCOL_15,
2530*ed01e57aSRicardo Ribalda .driver_info = (kernel_ulong_t)&(const struct uvc_device_info){
2531*ed01e57aSRicardo Ribalda .uvc_version = 0x010a,
2532*ed01e57aSRicardo Ribalda } },
2533*ed01e57aSRicardo Ribalda /* Quanta ACER HD User Facing */
2534*ed01e57aSRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
2535*ed01e57aSRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
2536*ed01e57aSRicardo Ribalda .idVendor = 0x0408,
25379471b8f8SLaurent Pinchart .idProduct = 0x4035,
25389471b8f8SLaurent Pinchart .bInterfaceClass = USB_CLASS_VIDEO,
25399471b8f8SLaurent Pinchart .bInterfaceSubClass = 1,
25409471b8f8SLaurent Pinchart .bInterfaceProtocol = UVC_PC_PROTOCOL_15,
25419471b8f8SLaurent Pinchart .driver_info = (kernel_ulong_t)&(const struct uvc_device_info){
25429471b8f8SLaurent Pinchart .uvc_version = 0x010a,
25439471b8f8SLaurent Pinchart } },
25440c0d06caSMauro Carvalho Chehab /* LogiLink Wireless Webcam */
25450c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
25460c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
25470c0d06caSMauro Carvalho Chehab .idVendor = 0x0416,
25480c0d06caSMauro Carvalho Chehab .idProduct = 0xa91a,
25490c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
25500c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
25510c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
25523bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax },
25530c0d06caSMauro Carvalho Chehab /* Genius eFace 2025 */
25540c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
25550c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
25560c0d06caSMauro Carvalho Chehab .idVendor = 0x0458,
25570c0d06caSMauro Carvalho Chehab .idProduct = 0x706e,
25580c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
25590c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
25600c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
25613bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax },
25620c0d06caSMauro Carvalho Chehab /* Microsoft Lifecam NX-6000 */
25630c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
25640c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
25650c0d06caSMauro Carvalho Chehab .idVendor = 0x045e,
25660c0d06caSMauro Carvalho Chehab .idProduct = 0x00f8,
25670c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
25680c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
25690c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
25703bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax },
25711558ec83SLaurent Pinchart /* Microsoft Lifecam NX-3000 */
25721558ec83SLaurent Pinchart { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
25731558ec83SLaurent Pinchart | USB_DEVICE_ID_MATCH_INT_INFO,
25741558ec83SLaurent Pinchart .idVendor = 0x045e,
25751558ec83SLaurent Pinchart .idProduct = 0x0721,
25761558ec83SLaurent Pinchart .bInterfaceClass = USB_CLASS_VIDEO,
25771558ec83SLaurent Pinchart .bInterfaceSubClass = 1,
25781558ec83SLaurent Pinchart .bInterfaceProtocol = 0,
25793bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def },
25800c0d06caSMauro Carvalho Chehab /* Microsoft Lifecam VX-7000 */
25810c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
25820c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
25830c0d06caSMauro Carvalho Chehab .idVendor = 0x045e,
25840c0d06caSMauro Carvalho Chehab .idProduct = 0x0723,
25850c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
25860c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
25870c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
25883bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax },
2589136effa7SRicardo Ribalda /* Logitech, Webcam C910 */
2590136effa7SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
2591136effa7SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
2592136effa7SRicardo Ribalda .idVendor = 0x046d,
2593136effa7SRicardo Ribalda .idProduct = 0x0821,
2594136effa7SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
2595136effa7SRicardo Ribalda .bInterfaceSubClass = 1,
2596136effa7SRicardo Ribalda .bInterfaceProtocol = 0,
2597136effa7SRicardo Ribalda .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_WAKE_AUTOSUSPEND)},
2598136effa7SRicardo Ribalda /* Logitech, Webcam B910 */
2599136effa7SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
2600136effa7SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
2601136effa7SRicardo Ribalda .idVendor = 0x046d,
2602136effa7SRicardo Ribalda .idProduct = 0x0823,
2603136effa7SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
2604136effa7SRicardo Ribalda .bInterfaceSubClass = 1,
2605136effa7SRicardo Ribalda .bInterfaceProtocol = 0,
2606136effa7SRicardo Ribalda .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_WAKE_AUTOSUSPEND)},
26070c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Fusion */
26080c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
26090c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
26100c0d06caSMauro Carvalho Chehab .idVendor = 0x046d,
26110c0d06caSMauro Carvalho Chehab .idProduct = 0x08c1,
26120c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
26130c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
26140c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 },
26150c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Orbit MP */
26160c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
26170c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
26180c0d06caSMauro Carvalho Chehab .idVendor = 0x046d,
26190c0d06caSMauro Carvalho Chehab .idProduct = 0x08c2,
26200c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
26210c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
26220c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 },
26230c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Pro for Notebook */
26240c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
26250c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
26260c0d06caSMauro Carvalho Chehab .idVendor = 0x046d,
26270c0d06caSMauro Carvalho Chehab .idProduct = 0x08c3,
26280c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
26290c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
26300c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 },
26310c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Pro 5000 */
26320c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
26330c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
26340c0d06caSMauro Carvalho Chehab .idVendor = 0x046d,
26350c0d06caSMauro Carvalho Chehab .idProduct = 0x08c5,
26360c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
26370c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
26380c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 },
26390c0d06caSMauro Carvalho Chehab /* Logitech Quickcam OEM Dell Notebook */
26400c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
26410c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
26420c0d06caSMauro Carvalho Chehab .idVendor = 0x046d,
26430c0d06caSMauro Carvalho Chehab .idProduct = 0x08c6,
26440c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
26450c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
26460c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 },
26470c0d06caSMauro Carvalho Chehab /* Logitech Quickcam OEM Cisco VT Camera II */
26480c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
26490c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
26500c0d06caSMauro Carvalho Chehab .idVendor = 0x046d,
26510c0d06caSMauro Carvalho Chehab .idProduct = 0x08c7,
26520c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
26530c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
26540c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 },
265517e1319fSWilliam Manley /* Logitech HD Pro Webcam C920 */
265617e1319fSWilliam Manley { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
265717e1319fSWilliam Manley | USB_DEVICE_ID_MATCH_INT_INFO,
265817e1319fSWilliam Manley .idVendor = 0x046d,
265917e1319fSWilliam Manley .idProduct = 0x082d,
266017e1319fSWilliam Manley .bInterfaceClass = USB_CLASS_VIDEO,
266117e1319fSWilliam Manley .bInterfaceSubClass = 1,
266217e1319fSWilliam Manley .bInterfaceProtocol = 0,
26636dbe1b76SOleksandr Natalenko .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT
26646dbe1b76SOleksandr Natalenko | UVC_QUIRK_INVALID_DEVICE_SOF) },
26657b0155feSRicardo Ribalda /* Logitech HD Pro Webcam C922 */
26667b0155feSRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
26677b0155feSRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
26687b0155feSRicardo Ribalda .idVendor = 0x046d,
26697b0155feSRicardo Ribalda .idProduct = 0x085c,
26707b0155feSRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
26717b0155feSRicardo Ribalda .bInterfaceSubClass = 1,
26727b0155feSRicardo Ribalda .bInterfaceProtocol = 0,
26737b0155feSRicardo Ribalda .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_INVALID_DEVICE_SOF) },
26744b3421c2SRicardo Ribalda /* Logitech Rally Bar Huddle */
26754b3421c2SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
26764b3421c2SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
26774b3421c2SRicardo Ribalda .idVendor = 0x046d,
26784b3421c2SRicardo Ribalda .idProduct = 0x087c,
26794b3421c2SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
26804b3421c2SRicardo Ribalda .bInterfaceSubClass = 1,
26814b3421c2SRicardo Ribalda .bInterfaceProtocol = 0,
26824b3421c2SRicardo Ribalda .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_NO_RESET_RESUME) },
26834b3421c2SRicardo Ribalda /* Logitech Rally Bar */
26844b3421c2SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
26854b3421c2SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
26864b3421c2SRicardo Ribalda .idVendor = 0x046d,
26874b3421c2SRicardo Ribalda .idProduct = 0x089b,
26884b3421c2SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
26894b3421c2SRicardo Ribalda .bInterfaceSubClass = 1,
26904b3421c2SRicardo Ribalda .bInterfaceProtocol = 0,
26914b3421c2SRicardo Ribalda .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_NO_RESET_RESUME) },
26924b3421c2SRicardo Ribalda /* Logitech Rally Bar Mini */
26934b3421c2SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
26944b3421c2SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
26954b3421c2SRicardo Ribalda .idVendor = 0x046d,
26964b3421c2SRicardo Ribalda .idProduct = 0x08d3,
26974b3421c2SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
26984b3421c2SRicardo Ribalda .bInterfaceSubClass = 1,
26994b3421c2SRicardo Ribalda .bInterfaceProtocol = 0,
27004b3421c2SRicardo Ribalda .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_NO_RESET_RESUME) },
27010c0d06caSMauro Carvalho Chehab /* Chicony CNF7129 (Asus EEE 100HE) */
27020c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
27030c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
27040c0d06caSMauro Carvalho Chehab .idVendor = 0x04f2,
27050c0d06caSMauro Carvalho Chehab .idProduct = 0xb071,
27060c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
27070c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
27080c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
270988d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTRICT_FRAME_RATE) },
27109f22f959SRicardo Ribalda /* Chicony EasyCamera */
27119f22f959SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
27129f22f959SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
27139f22f959SRicardo Ribalda .idVendor = 0x04f2,
2714332a2235SRicardo Ribalda .idProduct = 0xb5eb,
2715332a2235SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
2716332a2235SRicardo Ribalda .bInterfaceSubClass = 1,
2717332a2235SRicardo Ribalda .bInterfaceProtocol = 0,
2718332a2235SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
2719051400c3SRicardo Ribalda /* Chicony Electronics Co., Ltd Integrated Camera */
2720051400c3SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
2721051400c3SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
2722051400c3SRicardo Ribalda .idVendor = 0x04f2,
2723051400c3SRicardo Ribalda .idProduct = 0xb67c,
2724051400c3SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
2725051400c3SRicardo Ribalda .bInterfaceSubClass = 1,
2726051400c3SRicardo Ribalda .bInterfaceProtocol = UVC_PC_PROTOCOL_15,
2727051400c3SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 },
2728332a2235SRicardo Ribalda /* Chicony EasyCamera */
2729332a2235SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
2730332a2235SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
2731332a2235SRicardo Ribalda .idVendor = 0x04f2,
27329f22f959SRicardo Ribalda .idProduct = 0xb6ba,
27339f22f959SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
27349f22f959SRicardo Ribalda .bInterfaceSubClass = 1,
27359f22f959SRicardo Ribalda .bInterfaceProtocol = 0,
27369f22f959SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
27379f22f959SRicardo Ribalda /* Chicony EasyCamera */
27389f22f959SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
27399f22f959SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
27409f22f959SRicardo Ribalda .idVendor = 0x04f2,
27419f22f959SRicardo Ribalda .idProduct = 0xb746,
27429f22f959SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
27439f22f959SRicardo Ribalda .bInterfaceSubClass = 1,
27449f22f959SRicardo Ribalda .bInterfaceProtocol = 0,
27459f22f959SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
27460c0d06caSMauro Carvalho Chehab /* Alcor Micro AU3820 (Future Boy PC USB Webcam) */
27470c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
27480c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
27490c0d06caSMauro Carvalho Chehab .idVendor = 0x058f,
27500c0d06caSMauro Carvalho Chehab .idProduct = 0x3820,
27510c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
27520c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
27530c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
27543bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax },
27550c0d06caSMauro Carvalho Chehab /* Dell XPS m1530 */
27560c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
27570c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
27580c0d06caSMauro Carvalho Chehab .idVendor = 0x05a9,
27590c0d06caSMauro Carvalho Chehab .idProduct = 0x2640,
27600c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
27610c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
27620c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
27633bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def },
276489e0f248SJoseph Salisbury /* Dell SP2008WFP Monitor */
276589e0f248SJoseph Salisbury { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
276689e0f248SJoseph Salisbury | USB_DEVICE_ID_MATCH_INT_INFO,
276789e0f248SJoseph Salisbury .idVendor = 0x05a9,
276889e0f248SJoseph Salisbury .idProduct = 0x2641,
276989e0f248SJoseph Salisbury .bInterfaceClass = USB_CLASS_VIDEO,
277089e0f248SJoseph Salisbury .bInterfaceSubClass = 1,
277189e0f248SJoseph Salisbury .bInterfaceProtocol = 0,
27723bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def },
2773c2a273b2SJoseph Salisbury /* Dell Alienware X51 */
2774c2a273b2SJoseph Salisbury { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
2775c2a273b2SJoseph Salisbury | USB_DEVICE_ID_MATCH_INT_INFO,
2776c2a273b2SJoseph Salisbury .idVendor = 0x05a9,
2777c2a273b2SJoseph Salisbury .idProduct = 0x2643,
2778c2a273b2SJoseph Salisbury .bInterfaceClass = USB_CLASS_VIDEO,
2779c2a273b2SJoseph Salisbury .bInterfaceSubClass = 1,
2780c2a273b2SJoseph Salisbury .bInterfaceProtocol = 0,
27813bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def },
2782afcf44c7SKamal Mostafa /* Dell Studio Hybrid 140g (OmniVision webcam) */
2783afcf44c7SKamal Mostafa { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
2784afcf44c7SKamal Mostafa | USB_DEVICE_ID_MATCH_INT_INFO,
2785afcf44c7SKamal Mostafa .idVendor = 0x05a9,
2786afcf44c7SKamal Mostafa .idProduct = 0x264a,
2787afcf44c7SKamal Mostafa .bInterfaceClass = USB_CLASS_VIDEO,
2788afcf44c7SKamal Mostafa .bInterfaceSubClass = 1,
2789afcf44c7SKamal Mostafa .bInterfaceProtocol = 0,
27903bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def },
279162ea864fSPaul Fertser /* Dell XPS M1330 (OmniVision OV7670 webcam) */
279262ea864fSPaul Fertser { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
279362ea864fSPaul Fertser | USB_DEVICE_ID_MATCH_INT_INFO,
279462ea864fSPaul Fertser .idVendor = 0x05a9,
279562ea864fSPaul Fertser .idProduct = 0x7670,
279662ea864fSPaul Fertser .bInterfaceClass = USB_CLASS_VIDEO,
279762ea864fSPaul Fertser .bInterfaceSubClass = 1,
279862ea864fSPaul Fertser .bInterfaceProtocol = 0,
27993bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def },
28000c0d06caSMauro Carvalho Chehab /* Apple Built-In iSight */
28010c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
28020c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
28030c0d06caSMauro Carvalho Chehab .idVendor = 0x05ac,
28040c0d06caSMauro Carvalho Chehab .idProduct = 0x8501,
28050c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
28060c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
28070c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
280888d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX
28093bc85817SGuennadi Liakhovetski | UVC_QUIRK_BUILTIN_ISIGHT) },
281053c26454SPaul Pawlowski /* Apple FaceTime HD Camera (Built-In) */
281153c26454SPaul Pawlowski { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
281253c26454SPaul Pawlowski | USB_DEVICE_ID_MATCH_INT_INFO,
281353c26454SPaul Pawlowski .idVendor = 0x05ac,
281453c26454SPaul Pawlowski .idProduct = 0x8514,
281553c26454SPaul Pawlowski .bInterfaceClass = USB_CLASS_VIDEO,
281653c26454SPaul Pawlowski .bInterfaceSubClass = 1,
281753c26454SPaul Pawlowski .bInterfaceProtocol = 0,
281853c26454SPaul Pawlowski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def },
28197b848ed6SDaniel Roschka /* Apple Built-In iSight via iBridge */
28207b848ed6SDaniel Roschka { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
28217b848ed6SDaniel Roschka | USB_DEVICE_ID_MATCH_INT_INFO,
28227b848ed6SDaniel Roschka .idVendor = 0x05ac,
28237b848ed6SDaniel Roschka .idProduct = 0x8600,
28247b848ed6SDaniel Roschka .bInterfaceClass = USB_CLASS_VIDEO,
28257b848ed6SDaniel Roschka .bInterfaceSubClass = 1,
28267b848ed6SDaniel Roschka .bInterfaceProtocol = 0,
28273bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def },
28280c0d06caSMauro Carvalho Chehab /* Foxlink ("HP Webcam" on HP Mini 5103) */
28290c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
28300c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
28310c0d06caSMauro Carvalho Chehab .idVendor = 0x05c8,
28320c0d06caSMauro Carvalho Chehab .idProduct = 0x0403,
28330c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
28340c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
28350c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
28363bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth },
28370c0d06caSMauro Carvalho Chehab /* Genesys Logic USB 2.0 PC Camera */
28380c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
28390c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
28400c0d06caSMauro Carvalho Chehab .idVendor = 0x05e3,
28410c0d06caSMauro Carvalho Chehab .idProduct = 0x0505,
28420c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
28430c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
28440c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
28453bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid },
28460c0d06caSMauro Carvalho Chehab /* Hercules Classic Silver */
28470c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
28480c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
28490c0d06caSMauro Carvalho Chehab .idVendor = 0x06f8,
28500c0d06caSMauro Carvalho Chehab .idProduct = 0x300c,
28510c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
28520c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
28530c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
28543bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth },
28550c0d06caSMauro Carvalho Chehab /* ViMicro Vega */
28560c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
28570c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
28580c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8,
28590c0d06caSMauro Carvalho Chehab .idProduct = 0x332d,
28600c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
28610c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
28620c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
28633bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth },
28640c0d06caSMauro Carvalho Chehab /* ViMicro - Minoru3D */
28650c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
28660c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
28670c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8,
28680c0d06caSMauro Carvalho Chehab .idProduct = 0x3410,
28690c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
28700c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
28710c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
28723bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth },
28730c0d06caSMauro Carvalho Chehab /* ViMicro Venus - Minoru3D */
28740c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
28750c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
28760c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8,
28770c0d06caSMauro Carvalho Chehab .idProduct = 0x3420,
28780c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
28790c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
28800c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
28813bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth },
28820c0d06caSMauro Carvalho Chehab /* Ophir Optronics - SPCAM 620U */
28830c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
28840c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
28850c0d06caSMauro Carvalho Chehab .idVendor = 0x0bd3,
28860c0d06caSMauro Carvalho Chehab .idProduct = 0x0555,
28870c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
28880c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
28890c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
28903bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax },
28910c0d06caSMauro Carvalho Chehab /* MT6227 */
28920c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
28930c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
28940c0d06caSMauro Carvalho Chehab .idVendor = 0x0e8d,
28950c0d06caSMauro Carvalho Chehab .idProduct = 0x0004,
28960c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
28970c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
28980c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
289988d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX
29003bc85817SGuennadi Liakhovetski | UVC_QUIRK_PROBE_DEF) },
29010c0d06caSMauro Carvalho Chehab /* IMC Networks (Medion Akoya) */
29020c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
29030c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
29040c0d06caSMauro Carvalho Chehab .idVendor = 0x13d3,
29050c0d06caSMauro Carvalho Chehab .idProduct = 0x5103,
29060c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
29070c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
29080c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
29093bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid },
29100c0d06caSMauro Carvalho Chehab /* JMicron USB2.0 XGA WebCam */
29110c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
29120c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
29130c0d06caSMauro Carvalho Chehab .idVendor = 0x152d,
29140c0d06caSMauro Carvalho Chehab .idProduct = 0x0310,
29150c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
29160c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
29170c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
29183bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax },
29190c0d06caSMauro Carvalho Chehab /* Syntek (HP Spartan) */
29200c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
29210c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
29220c0d06caSMauro Carvalho Chehab .idVendor = 0x174f,
29230c0d06caSMauro Carvalho Chehab .idProduct = 0x5212,
29240c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
29250c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
29260c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
29273bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid },
29280c0d06caSMauro Carvalho Chehab /* Syntek (Samsung Q310) */
29290c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
29300c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
29310c0d06caSMauro Carvalho Chehab .idVendor = 0x174f,
29320c0d06caSMauro Carvalho Chehab .idProduct = 0x5931,
29330c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
29340c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
29350c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
29363bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid },
29370c0d06caSMauro Carvalho Chehab /* Syntek (Packard Bell EasyNote MX52 */
29380c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
29390c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
29400c0d06caSMauro Carvalho Chehab .idVendor = 0x174f,
29410c0d06caSMauro Carvalho Chehab .idProduct = 0x8a12,
29420c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
29430c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
29440c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
29453bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid },
29460c0d06caSMauro Carvalho Chehab /* Syntek (Asus F9SG) */
29470c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
29480c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
29490c0d06caSMauro Carvalho Chehab .idVendor = 0x174f,
29500c0d06caSMauro Carvalho Chehab .idProduct = 0x8a31,
29510c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
29520c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
29530c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
29543bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid },
29550c0d06caSMauro Carvalho Chehab /* Syntek (Asus U3S) */
29560c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
29570c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
29580c0d06caSMauro Carvalho Chehab .idVendor = 0x174f,
29590c0d06caSMauro Carvalho Chehab .idProduct = 0x8a33,
29600c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
29610c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
29620c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
29633bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid },
29640c0d06caSMauro Carvalho Chehab /* Syntek (JAOtech Smart Terminal) */
29650c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
29660c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
29670c0d06caSMauro Carvalho Chehab .idVendor = 0x174f,
29680c0d06caSMauro Carvalho Chehab .idProduct = 0x8a34,
29690c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
29700c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
29710c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
29723bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid },
29730c0d06caSMauro Carvalho Chehab /* Miricle 307K */
29740c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
29750c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
29760c0d06caSMauro Carvalho Chehab .idVendor = 0x17dc,
29770c0d06caSMauro Carvalho Chehab .idProduct = 0x0202,
29780c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
29790c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
29800c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
29813bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid },
29820c0d06caSMauro Carvalho Chehab /* Lenovo Thinkpad SL400/SL500 */
29830c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
29840c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
29850c0d06caSMauro Carvalho Chehab .idVendor = 0x17ef,
29860c0d06caSMauro Carvalho Chehab .idProduct = 0x480b,
29870c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
29880c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
29890c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
29903bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid },
29910c0d06caSMauro Carvalho Chehab /* Aveo Technology USB 2.0 Camera */
29920c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
29930c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
29940c0d06caSMauro Carvalho Chehab .idVendor = 0x1871,
29950c0d06caSMauro Carvalho Chehab .idProduct = 0x0306,
29960c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
29970c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
29980c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
299988d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX
30003bc85817SGuennadi Liakhovetski | UVC_QUIRK_PROBE_EXTRAFIELDS) },
3001fe652471SLaurent Pinchart /* Aveo Technology USB 2.0 Camera (Tasco USB Microscope) */
3002fe652471SLaurent Pinchart { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
3003fe652471SLaurent Pinchart | USB_DEVICE_ID_MATCH_INT_INFO,
3004fe652471SLaurent Pinchart .idVendor = 0x1871,
3005fe652471SLaurent Pinchart .idProduct = 0x0516,
3006fe652471SLaurent Pinchart .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
3007fe652471SLaurent Pinchart .bInterfaceSubClass = 1,
3008fe652471SLaurent Pinchart .bInterfaceProtocol = 0 },
30090c0d06caSMauro Carvalho Chehab /* Ecamm Pico iMage */
30100c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
30110c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
30120c0d06caSMauro Carvalho Chehab .idVendor = 0x18cd,
30130c0d06caSMauro Carvalho Chehab .idProduct = 0xcafe,
30140c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
30150c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
30160c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
301788d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_EXTRAFIELDS) },
30180c0d06caSMauro Carvalho Chehab /* Manta MM-353 Plako */
30190c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
30200c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
30210c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec,
30220c0d06caSMauro Carvalho Chehab .idProduct = 0x3188,
30230c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
30240c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
30250c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
30263bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax },
30270c0d06caSMauro Carvalho Chehab /* FSC WebCam V30S */
30280c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
30290c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
30300c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec,
30310c0d06caSMauro Carvalho Chehab .idProduct = 0x3288,
30320c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
30330c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
30340c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
30353bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax },
30360c0d06caSMauro Carvalho Chehab /* Arkmicro unbranded */
30370c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
30380c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
30390c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec,
30400c0d06caSMauro Carvalho Chehab .idProduct = 0x3290,
30410c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
30420c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
30430c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
30443bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def },
30450c0d06caSMauro Carvalho Chehab /* The Imaging Source USB CCD cameras */
30460c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
30470c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
30480c0d06caSMauro Carvalho Chehab .idVendor = 0x199e,
30490c0d06caSMauro Carvalho Chehab .idProduct = 0x8102,
30500c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
30510c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
30520c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 },
30530c0d06caSMauro Carvalho Chehab /* Bodelin ProScopeHR */
30540c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
30550c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_DEV_HI
30560c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
30570c0d06caSMauro Carvalho Chehab .idVendor = 0x19ab,
30580c0d06caSMauro Carvalho Chehab .idProduct = 0x1000,
30590c0d06caSMauro Carvalho Chehab .bcdDevice_hi = 0x0126,
30600c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
30610c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
30620c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
306388d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_STATUS_INTERVAL) },
30640c0d06caSMauro Carvalho Chehab /* MSI StarCam 370i */
30650c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
30660c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
30670c0d06caSMauro Carvalho Chehab .idVendor = 0x1b3b,
30680c0d06caSMauro Carvalho Chehab .idProduct = 0x2951,
30690c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
30700c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
30710c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
30723bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax },
3073589266bdSNeil Armstrong /* Generalplus Technology Inc. 808 Camera */
3074589266bdSNeil Armstrong { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
3075589266bdSNeil Armstrong | USB_DEVICE_ID_MATCH_INT_INFO,
3076589266bdSNeil Armstrong .idVendor = 0x1b3f,
3077589266bdSNeil Armstrong .idProduct = 0x2002,
3078589266bdSNeil Armstrong .bInterfaceClass = USB_CLASS_VIDEO,
3079589266bdSNeil Armstrong .bInterfaceSubClass = 1,
3080589266bdSNeil Armstrong .bInterfaceProtocol = 0,
3081589266bdSNeil Armstrong .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax },
3082b400b6f2SLaurent Pinchart /* Shenzhen Aoni Electronic Co.,Ltd 2K FHD camera */
3083b400b6f2SLaurent Pinchart { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
3084b400b6f2SLaurent Pinchart | USB_DEVICE_ID_MATCH_INT_INFO,
3085b400b6f2SLaurent Pinchart .idVendor = 0x1bcf,
3086b400b6f2SLaurent Pinchart .idProduct = 0x0b40,
3087b400b6f2SLaurent Pinchart .bInterfaceClass = USB_CLASS_VIDEO,
3088b400b6f2SLaurent Pinchart .bInterfaceSubClass = 1,
3089b400b6f2SLaurent Pinchart .bInterfaceProtocol = 0,
3090b400b6f2SLaurent Pinchart .driver_info = (kernel_ulong_t)&(const struct uvc_device_info){
3091b400b6f2SLaurent Pinchart .uvc_version = 0x010a,
3092b400b6f2SLaurent Pinchart } },
30930c0d06caSMauro Carvalho Chehab /* SiGma Micro USB Web Camera */
30940c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
30950c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO,
30960c0d06caSMauro Carvalho Chehab .idVendor = 0x1c4f,
30970c0d06caSMauro Carvalho Chehab .idProduct = 0x3000,
30980c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO,
30990c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1,
31000c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0,
310188d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX
31023bc85817SGuennadi Liakhovetski | UVC_QUIRK_IGNORE_SELECTOR_UNIT) },
310331f9b4a2SDavid Given /* NXP Semiconductors IR VIDEO */
310431f9b4a2SDavid Given { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
310531f9b4a2SDavid Given | USB_DEVICE_ID_MATCH_INT_INFO,
310631f9b4a2SDavid Given .idVendor = 0x1fc9,
310731f9b4a2SDavid Given .idProduct = 0x009b,
310831f9b4a2SDavid Given .bInterfaceClass = USB_CLASS_VIDEO,
310931f9b4a2SDavid Given .bInterfaceSubClass = 1,
311031f9b4a2SDavid Given .bInterfaceProtocol = 0,
311131f9b4a2SDavid Given .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax },
3112e1b78a33SPhilipp Zabel /* Oculus VR Positional Tracker DK2 */
3113e1b78a33SPhilipp Zabel { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
3114e1b78a33SPhilipp Zabel | USB_DEVICE_ID_MATCH_INT_INFO,
3115e1b78a33SPhilipp Zabel .idVendor = 0x2833,
3116e1b78a33SPhilipp Zabel .idProduct = 0x0201,
3117e1b78a33SPhilipp Zabel .bInterfaceClass = USB_CLASS_VIDEO,
3118e1b78a33SPhilipp Zabel .bInterfaceSubClass = 1,
3119e1b78a33SPhilipp Zabel .bInterfaceProtocol = 0,
31203bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_force_y8 },
312103c47aaeSPhilipp Zabel /* Oculus VR Rift Sensor */
312203c47aaeSPhilipp Zabel { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
312303c47aaeSPhilipp Zabel | USB_DEVICE_ID_MATCH_INT_INFO,
312403c47aaeSPhilipp Zabel .idVendor = 0x2833,
312503c47aaeSPhilipp Zabel .idProduct = 0x0211,
312603c47aaeSPhilipp Zabel .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
312703c47aaeSPhilipp Zabel .bInterfaceSubClass = 1,
312803c47aaeSPhilipp Zabel .bInterfaceProtocol = 0,
31293bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_force_y8 },
31301dd2e8f9SSergey Zakharchenko /* GEO Semiconductor GC6500 */
31311dd2e8f9SSergey Zakharchenko { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
31321dd2e8f9SSergey Zakharchenko | USB_DEVICE_ID_MATCH_INT_INFO,
31331dd2e8f9SSergey Zakharchenko .idVendor = 0x29fe,
31341dd2e8f9SSergey Zakharchenko .idProduct = 0x4d53,
31351dd2e8f9SSergey Zakharchenko .bInterfaceClass = USB_CLASS_VIDEO,
31361dd2e8f9SSergey Zakharchenko .bInterfaceSubClass = 1,
31371dd2e8f9SSergey Zakharchenko .bInterfaceProtocol = 0,
31381dd2e8f9SSergey Zakharchenko .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_FORCE_BPP) },
31395193d26aSRicardo Ribalda /* SunplusIT Inc HD Camera */
31405193d26aSRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
31415193d26aSRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
31425193d26aSRicardo Ribalda .idVendor = 0x2b7e,
31435193d26aSRicardo Ribalda .idProduct = 0xb752,
31445193d26aSRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
31455193d26aSRicardo Ribalda .bInterfaceSubClass = 1,
31465193d26aSRicardo Ribalda .bInterfaceProtocol = UVC_PC_PROTOCOL_15,
31475193d26aSRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 },
3148991df394SRicardo Ribalda /* Insta360 Link */
3149991df394SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
3150991df394SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
3151991df394SRicardo Ribalda .idVendor = 0x2e1a,
3152991df394SRicardo Ribalda .idProduct = 0x4c01,
3153991df394SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
3154991df394SRicardo Ribalda .bInterfaceSubClass = 1,
3155991df394SRicardo Ribalda .bInterfaceProtocol = 0,
3156991df394SRicardo Ribalda .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_DISABLE_AUTOSUSPEND) },
3157a7c28150SRicardo Ribalda /* Lenovo Integrated Camera */
3158a7c28150SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
3159a7c28150SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
3160a7c28150SRicardo Ribalda .idVendor = 0x30c9,
3161a7c28150SRicardo Ribalda .idProduct = 0x0093,
3162a7c28150SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
3163a7c28150SRicardo Ribalda .bInterfaceSubClass = 1,
3164a7c28150SRicardo Ribalda .bInterfaceProtocol = UVC_PC_PROTOCOL_15,
3165a7c28150SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 },
3166101418b3Shuanglei /* Sonix Technology USB 2.0 Camera */
3167101418b3Shuanglei { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
3168101418b3Shuanglei | USB_DEVICE_ID_MATCH_INT_INFO,
3169101418b3Shuanglei .idVendor = 0x3277,
3170101418b3Shuanglei .idProduct = 0x0072,
3171101418b3Shuanglei .bInterfaceClass = USB_CLASS_VIDEO,
3172101418b3Shuanglei .bInterfaceSubClass = 1,
3173101418b3Shuanglei .bInterfaceProtocol = 0,
3174101418b3Shuanglei .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
3175eff1e24cSRicardo Ribalda /* Acer EasyCamera */
3176eff1e24cSRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
3177eff1e24cSRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
3178eff1e24cSRicardo Ribalda .idVendor = 0x5986,
3179eff1e24cSRicardo Ribalda .idProduct = 0x1172,
3180eff1e24cSRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
3181eff1e24cSRicardo Ribalda .bInterfaceSubClass = 1,
3182eff1e24cSRicardo Ribalda .bInterfaceProtocol = 0,
3183eff1e24cSRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
318481e78a6fSRicardo Ribalda /* Acer EasyCamera */
318581e78a6fSRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
318681e78a6fSRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO,
318781e78a6fSRicardo Ribalda .idVendor = 0x5986,
318881e78a6fSRicardo Ribalda .idProduct = 0x1180,
318981e78a6fSRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO,
319081e78a6fSRicardo Ribalda .bInterfaceSubClass = 1,
319181e78a6fSRicardo Ribalda .bInterfaceProtocol = 0,
319281e78a6fSRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited },
3193e33ae66aSDmitry Perchanov /* Intel D410/ASR depth camera */
3194e33ae66aSDmitry Perchanov { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
3195e33ae66aSDmitry Perchanov | USB_DEVICE_ID_MATCH_INT_INFO,
3196e33ae66aSDmitry Perchanov .idVendor = 0x8086,
3197e33ae66aSDmitry Perchanov .idProduct = 0x0ad2,
3198e33ae66aSDmitry Perchanov .bInterfaceClass = USB_CLASS_VIDEO,
3199e33ae66aSDmitry Perchanov .bInterfaceSubClass = 1,
3200e33ae66aSDmitry Perchanov .bInterfaceProtocol = 0,
3201e33ae66aSDmitry Perchanov .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
3202e33ae66aSDmitry Perchanov /* Intel D415/ASRC depth camera */
3203e33ae66aSDmitry Perchanov { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
3204e33ae66aSDmitry Perchanov | USB_DEVICE_ID_MATCH_INT_INFO,
3205e33ae66aSDmitry Perchanov .idVendor = 0x8086,
3206e33ae66aSDmitry Perchanov .idProduct = 0x0ad3,
3207e33ae66aSDmitry Perchanov .bInterfaceClass = USB_CLASS_VIDEO,
3208e33ae66aSDmitry Perchanov .bInterfaceSubClass = 1,
3209e33ae66aSDmitry Perchanov .bInterfaceProtocol = 0,
3210e33ae66aSDmitry Perchanov .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
3211e33ae66aSDmitry Perchanov /* Intel D430/AWG depth camera */
3212e33ae66aSDmitry Perchanov { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
3213e33ae66aSDmitry Perchanov | USB_DEVICE_ID_MATCH_INT_INFO,
3214e33ae66aSDmitry Perchanov .idVendor = 0x8086,
3215e33ae66aSDmitry Perchanov .idProduct = 0x0ad4,
3216e33ae66aSDmitry Perchanov .bInterfaceClass = USB_CLASS_VIDEO,
3217e33ae66aSDmitry Perchanov .bInterfaceSubClass = 1,
3218e33ae66aSDmitry Perchanov .bInterfaceProtocol = 0,
3219e33ae66aSDmitry Perchanov .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
32206ea0d588SGuennadi Liakhovetski /* Intel RealSense D4M */
32216ea0d588SGuennadi Liakhovetski { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
32226ea0d588SGuennadi Liakhovetski | USB_DEVICE_ID_MATCH_INT_INFO,
32236ea0d588SGuennadi Liakhovetski .idVendor = 0x8086,
32246ea0d588SGuennadi Liakhovetski .idProduct = 0x0b03,
32256ea0d588SGuennadi Liakhovetski .bInterfaceClass = USB_CLASS_VIDEO,
32266ea0d588SGuennadi Liakhovetski .bInterfaceSubClass = 1,
32276ea0d588SGuennadi Liakhovetski .bInterfaceProtocol = 0,
32286ea0d588SGuennadi Liakhovetski .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
3229e33ae66aSDmitry Perchanov /* Intel D435/AWGC depth camera */
3230e33ae66aSDmitry Perchanov { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
3231e33ae66aSDmitry Perchanov | USB_DEVICE_ID_MATCH_INT_INFO,
3232e33ae66aSDmitry Perchanov .idVendor = 0x8086,
3233e33ae66aSDmitry Perchanov .idProduct = 0x0b07,
3234e33ae66aSDmitry Perchanov .bInterfaceClass = USB_CLASS_VIDEO,
3235e33ae66aSDmitry Perchanov .bInterfaceSubClass = 1,
3236e33ae66aSDmitry Perchanov .bInterfaceProtocol = 0,
3237e33ae66aSDmitry Perchanov .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
3238e33ae66aSDmitry Perchanov /* Intel D435i depth camera */
3239e33ae66aSDmitry Perchanov { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
3240e33ae66aSDmitry Perchanov | USB_DEVICE_ID_MATCH_INT_INFO,
3241e33ae66aSDmitry Perchanov .idVendor = 0x8086,
3242e33ae66aSDmitry Perchanov .idProduct = 0x0b3a,
3243e33ae66aSDmitry Perchanov .bInterfaceClass = USB_CLASS_VIDEO,
3244e33ae66aSDmitry Perchanov .bInterfaceSubClass = 1,
3245e33ae66aSDmitry Perchanov .bInterfaceProtocol = 0,
3246e33ae66aSDmitry Perchanov .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
3247e33ae66aSDmitry Perchanov /* Intel D405 Depth Camera */
3248e33ae66aSDmitry Perchanov { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
3249e33ae66aSDmitry Perchanov | USB_DEVICE_ID_MATCH_INT_INFO,
3250e33ae66aSDmitry Perchanov .idVendor = 0x8086,
3251e33ae66aSDmitry Perchanov .idProduct = 0x0b5b,
3252e33ae66aSDmitry Perchanov .bInterfaceClass = USB_CLASS_VIDEO,
3253e33ae66aSDmitry Perchanov .bInterfaceSubClass = 1,
3254e33ae66aSDmitry Perchanov .bInterfaceProtocol = 0,
3255e33ae66aSDmitry Perchanov .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
3256e33ae66aSDmitry Perchanov /* Intel D455 Depth Camera */
3257e33ae66aSDmitry Perchanov { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
3258e33ae66aSDmitry Perchanov | USB_DEVICE_ID_MATCH_INT_INFO,
3259e33ae66aSDmitry Perchanov .idVendor = 0x8086,
3260e33ae66aSDmitry Perchanov .idProduct = 0x0b5c,
3261e33ae66aSDmitry Perchanov .bInterfaceClass = USB_CLASS_VIDEO,
3262e33ae66aSDmitry Perchanov .bInterfaceSubClass = 1,
3263e33ae66aSDmitry Perchanov .bInterfaceProtocol = 0,
3264e33ae66aSDmitry Perchanov .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
3265f20f3416SDmitry Perchanov /* Intel D421 Depth Module */
3266f20f3416SDmitry Perchanov { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
3267f20f3416SDmitry Perchanov | USB_DEVICE_ID_MATCH_INT_INFO,
3268f20f3416SDmitry Perchanov .idVendor = 0x8086,
3269f20f3416SDmitry Perchanov .idProduct = 0x1155,
3270f20f3416SDmitry Perchanov .bInterfaceClass = USB_CLASS_VIDEO,
3271f20f3416SDmitry Perchanov .bInterfaceSubClass = 1,
3272f20f3416SDmitry Perchanov .bInterfaceProtocol = 0,
3273f20f3416SDmitry Perchanov .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) },
32740c0d06caSMauro Carvalho Chehab /* Generic USB Video Class */
32758afe97beSLaurent Pinchart { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_UNDEFINED) },
32768afe97beSLaurent Pinchart { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_15) },
32770c0d06caSMauro Carvalho Chehab {}
32780c0d06caSMauro Carvalho Chehab };
32790c0d06caSMauro Carvalho Chehab
32800c0d06caSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, uvc_ids);
32810c0d06caSMauro Carvalho Chehab
32820c0d06caSMauro Carvalho Chehab struct uvc_driver uvc_driver = {
32830c0d06caSMauro Carvalho Chehab .driver = {
32840c0d06caSMauro Carvalho Chehab .name = "uvcvideo",
32850c0d06caSMauro Carvalho Chehab .probe = uvc_probe,
32860c0d06caSMauro Carvalho Chehab .disconnect = uvc_disconnect,
32870c0d06caSMauro Carvalho Chehab .suspend = uvc_suspend,
32880c0d06caSMauro Carvalho Chehab .resume = uvc_resume,
32890c0d06caSMauro Carvalho Chehab .reset_resume = uvc_reset_resume,
32900c0d06caSMauro Carvalho Chehab .id_table = uvc_ids,
32910c0d06caSMauro Carvalho Chehab .supports_autosuspend = 1,
32920c0d06caSMauro Carvalho Chehab },
32930c0d06caSMauro Carvalho Chehab };
32940c0d06caSMauro Carvalho Chehab
uvc_init(void)32950c0d06caSMauro Carvalho Chehab static int __init uvc_init(void)
32960c0d06caSMauro Carvalho Chehab {
32970c0d06caSMauro Carvalho Chehab int ret;
32980c0d06caSMauro Carvalho Chehab
32990c0d06caSMauro Carvalho Chehab uvc_debugfs_init();
33000c0d06caSMauro Carvalho Chehab
33010c0d06caSMauro Carvalho Chehab ret = usb_register(&uvc_driver.driver);
33020c0d06caSMauro Carvalho Chehab if (ret < 0) {
33030c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup();
33040c0d06caSMauro Carvalho Chehab return ret;
33050c0d06caSMauro Carvalho Chehab }
33060c0d06caSMauro Carvalho Chehab
33070c0d06caSMauro Carvalho Chehab return 0;
33080c0d06caSMauro Carvalho Chehab }
33090c0d06caSMauro Carvalho Chehab
uvc_cleanup(void)33100c0d06caSMauro Carvalho Chehab static void __exit uvc_cleanup(void)
33110c0d06caSMauro Carvalho Chehab {
33120c0d06caSMauro Carvalho Chehab usb_deregister(&uvc_driver.driver);
33130c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup();
33140c0d06caSMauro Carvalho Chehab }
33150c0d06caSMauro Carvalho Chehab
33160c0d06caSMauro Carvalho Chehab module_init(uvc_init);
33170c0d06caSMauro Carvalho Chehab module_exit(uvc_cleanup);
33180c0d06caSMauro Carvalho Chehab
33190c0d06caSMauro Carvalho Chehab MODULE_AUTHOR(DRIVER_AUTHOR);
33200c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION(DRIVER_DESC);
33210c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL");
33220c0d06caSMauro Carvalho Chehab MODULE_VERSION(DRIVER_VERSION);
33230c0d06caSMauro Carvalho Chehab
3324