162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *      uvc_driver.c  --  USB Video Class driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *      Copyright (C) 2005-2010
662306a36Sopenharmony_ci *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/atomic.h>
1062306a36Sopenharmony_ci#include <linux/bits.h>
1162306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/list.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/usb.h>
1762306a36Sopenharmony_ci#include <linux/usb/uvc.h>
1862306a36Sopenharmony_ci#include <linux/videodev2.h>
1962306a36Sopenharmony_ci#include <linux/vmalloc.h>
2062306a36Sopenharmony_ci#include <linux/wait.h>
2162306a36Sopenharmony_ci#include <asm/unaligned.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <media/v4l2-common.h>
2462306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "uvcvideo.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define DRIVER_AUTHOR		"Laurent Pinchart " \
2962306a36Sopenharmony_ci				"<laurent.pinchart@ideasonboard.com>"
3062306a36Sopenharmony_ci#define DRIVER_DESC		"USB Video Class driver"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ciunsigned int uvc_clock_param = CLOCK_MONOTONIC;
3362306a36Sopenharmony_ciunsigned int uvc_hw_timestamps_param;
3462306a36Sopenharmony_ciunsigned int uvc_no_drop_param;
3562306a36Sopenharmony_cistatic unsigned int uvc_quirks_param = -1;
3662306a36Sopenharmony_ciunsigned int uvc_dbg_param;
3762306a36Sopenharmony_ciunsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* ------------------------------------------------------------------------
4062306a36Sopenharmony_ci * Utility functions
4162306a36Sopenharmony_ci */
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistruct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
4462306a36Sopenharmony_ci		u8 epaddr)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct usb_host_endpoint *ep;
4762306a36Sopenharmony_ci	unsigned int i;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
5062306a36Sopenharmony_ci		ep = &alts->endpoint[i];
5162306a36Sopenharmony_ci		if (ep->desc.bEndpointAddress == epaddr)
5262306a36Sopenharmony_ci			return ep;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return NULL;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic enum v4l2_colorspace uvc_colorspace(const u8 primaries)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	static const enum v4l2_colorspace colorprimaries[] = {
6162306a36Sopenharmony_ci		V4L2_COLORSPACE_SRGB,  /* Unspecified */
6262306a36Sopenharmony_ci		V4L2_COLORSPACE_SRGB,
6362306a36Sopenharmony_ci		V4L2_COLORSPACE_470_SYSTEM_M,
6462306a36Sopenharmony_ci		V4L2_COLORSPACE_470_SYSTEM_BG,
6562306a36Sopenharmony_ci		V4L2_COLORSPACE_SMPTE170M,
6662306a36Sopenharmony_ci		V4L2_COLORSPACE_SMPTE240M,
6762306a36Sopenharmony_ci	};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if (primaries < ARRAY_SIZE(colorprimaries))
7062306a36Sopenharmony_ci		return colorprimaries[primaries];
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return V4L2_COLORSPACE_SRGB;  /* Reserved */
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic enum v4l2_xfer_func uvc_xfer_func(const u8 transfer_characteristics)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	/*
7862306a36Sopenharmony_ci	 * V4L2 does not currently have definitions for all possible values of
7962306a36Sopenharmony_ci	 * UVC transfer characteristics. If v4l2_xfer_func is extended with new
8062306a36Sopenharmony_ci	 * values, the mapping below should be updated.
8162306a36Sopenharmony_ci	 *
8262306a36Sopenharmony_ci	 * Substitutions are taken from the mapping given for
8362306a36Sopenharmony_ci	 * V4L2_XFER_FUNC_DEFAULT documented in videodev2.h.
8462306a36Sopenharmony_ci	 */
8562306a36Sopenharmony_ci	static const enum v4l2_xfer_func xfer_funcs[] = {
8662306a36Sopenharmony_ci		V4L2_XFER_FUNC_DEFAULT,    /* Unspecified */
8762306a36Sopenharmony_ci		V4L2_XFER_FUNC_709,
8862306a36Sopenharmony_ci		V4L2_XFER_FUNC_709,        /* Substitution for BT.470-2 M */
8962306a36Sopenharmony_ci		V4L2_XFER_FUNC_709,        /* Substitution for BT.470-2 B, G */
9062306a36Sopenharmony_ci		V4L2_XFER_FUNC_709,        /* Substitution for SMPTE 170M */
9162306a36Sopenharmony_ci		V4L2_XFER_FUNC_SMPTE240M,
9262306a36Sopenharmony_ci		V4L2_XFER_FUNC_NONE,
9362306a36Sopenharmony_ci		V4L2_XFER_FUNC_SRGB,
9462306a36Sopenharmony_ci	};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (transfer_characteristics < ARRAY_SIZE(xfer_funcs))
9762306a36Sopenharmony_ci		return xfer_funcs[transfer_characteristics];
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	return V4L2_XFER_FUNC_DEFAULT;  /* Reserved */
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic enum v4l2_ycbcr_encoding uvc_ycbcr_enc(const u8 matrix_coefficients)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	/*
10562306a36Sopenharmony_ci	 * V4L2 does not currently have definitions for all possible values of
10662306a36Sopenharmony_ci	 * UVC matrix coefficients. If v4l2_ycbcr_encoding is extended with new
10762306a36Sopenharmony_ci	 * values, the mapping below should be updated.
10862306a36Sopenharmony_ci	 *
10962306a36Sopenharmony_ci	 * Substitutions are taken from the mapping given for
11062306a36Sopenharmony_ci	 * V4L2_YCBCR_ENC_DEFAULT documented in videodev2.h.
11162306a36Sopenharmony_ci	 *
11262306a36Sopenharmony_ci	 * FCC is assumed to be close enough to 601.
11362306a36Sopenharmony_ci	 */
11462306a36Sopenharmony_ci	static const enum v4l2_ycbcr_encoding ycbcr_encs[] = {
11562306a36Sopenharmony_ci		V4L2_YCBCR_ENC_DEFAULT,  /* Unspecified */
11662306a36Sopenharmony_ci		V4L2_YCBCR_ENC_709,
11762306a36Sopenharmony_ci		V4L2_YCBCR_ENC_601,      /* Substitution for FCC */
11862306a36Sopenharmony_ci		V4L2_YCBCR_ENC_601,      /* Substitution for BT.470-2 B, G */
11962306a36Sopenharmony_ci		V4L2_YCBCR_ENC_601,
12062306a36Sopenharmony_ci		V4L2_YCBCR_ENC_SMPTE240M,
12162306a36Sopenharmony_ci	};
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (matrix_coefficients < ARRAY_SIZE(ycbcr_encs))
12462306a36Sopenharmony_ci		return ycbcr_encs[matrix_coefficients];
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	return V4L2_YCBCR_ENC_DEFAULT;  /* Reserved */
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/* ------------------------------------------------------------------------
13062306a36Sopenharmony_ci * Terminal and unit management
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistruct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct uvc_entity *entity;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	list_for_each_entry(entity, &dev->entities, list) {
13862306a36Sopenharmony_ci		if (entity->id == id)
13962306a36Sopenharmony_ci			return entity;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return NULL;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
14662306a36Sopenharmony_ci	int id, struct uvc_entity *entity)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	unsigned int i;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (entity == NULL)
15162306a36Sopenharmony_ci		entity = list_entry(&dev->entities, struct uvc_entity, list);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	list_for_each_entry_continue(entity, &dev->entities, list) {
15462306a36Sopenharmony_ci		for (i = 0; i < entity->bNrInPins; ++i)
15562306a36Sopenharmony_ci			if (entity->baSourceID[i] == id)
15662306a36Sopenharmony_ci				return entity;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	return NULL;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct uvc_streaming *stream;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	list_for_each_entry(stream, &dev->streams, list) {
16762306a36Sopenharmony_ci		if (stream->header.bTerminalLink == id)
16862306a36Sopenharmony_ci			return stream;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	return NULL;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci/* ------------------------------------------------------------------------
17562306a36Sopenharmony_ci * Streaming Object Management
17662306a36Sopenharmony_ci */
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic void uvc_stream_delete(struct uvc_streaming *stream)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	if (stream->async_wq)
18162306a36Sopenharmony_ci		destroy_workqueue(stream->async_wq);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	mutex_destroy(&stream->mutex);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	usb_put_intf(stream->intf);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	kfree(stream->formats);
18862306a36Sopenharmony_ci	kfree(stream->header.bmaControls);
18962306a36Sopenharmony_ci	kfree(stream);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic struct uvc_streaming *uvc_stream_new(struct uvc_device *dev,
19362306a36Sopenharmony_ci					    struct usb_interface *intf)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct uvc_streaming *stream;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
19862306a36Sopenharmony_ci	if (stream == NULL)
19962306a36Sopenharmony_ci		return NULL;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	mutex_init(&stream->mutex);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	stream->dev = dev;
20462306a36Sopenharmony_ci	stream->intf = usb_get_intf(intf);
20562306a36Sopenharmony_ci	stream->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/* Allocate a stream specific work queue for asynchronous tasks. */
20862306a36Sopenharmony_ci	stream->async_wq = alloc_workqueue("uvcvideo", WQ_UNBOUND | WQ_HIGHPRI,
20962306a36Sopenharmony_ci					   0);
21062306a36Sopenharmony_ci	if (!stream->async_wq) {
21162306a36Sopenharmony_ci		uvc_stream_delete(stream);
21262306a36Sopenharmony_ci		return NULL;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	return stream;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci/* ------------------------------------------------------------------------
21962306a36Sopenharmony_ci * Descriptors parsing
22062306a36Sopenharmony_ci */
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic int uvc_parse_format(struct uvc_device *dev,
22362306a36Sopenharmony_ci	struct uvc_streaming *streaming, struct uvc_format *format,
22462306a36Sopenharmony_ci	struct uvc_frame *frames, u32 **intervals, const unsigned char *buffer,
22562306a36Sopenharmony_ci	int buflen)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct usb_interface *intf = streaming->intf;
22862306a36Sopenharmony_ci	struct usb_host_interface *alts = intf->cur_altsetting;
22962306a36Sopenharmony_ci	const struct uvc_format_desc *fmtdesc;
23062306a36Sopenharmony_ci	struct uvc_frame *frame;
23162306a36Sopenharmony_ci	const unsigned char *start = buffer;
23262306a36Sopenharmony_ci	unsigned int width_multiplier = 1;
23362306a36Sopenharmony_ci	unsigned int interval;
23462306a36Sopenharmony_ci	unsigned int i, n;
23562306a36Sopenharmony_ci	u8 ftype;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	format->type = buffer[2];
23862306a36Sopenharmony_ci	format->index = buffer[3];
23962306a36Sopenharmony_ci	format->frames = frames;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	switch (buffer[2]) {
24262306a36Sopenharmony_ci	case UVC_VS_FORMAT_UNCOMPRESSED:
24362306a36Sopenharmony_ci	case UVC_VS_FORMAT_FRAME_BASED:
24462306a36Sopenharmony_ci		n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28;
24562306a36Sopenharmony_ci		if (buflen < n) {
24662306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
24762306a36Sopenharmony_ci				"device %d videostreaming interface %d FORMAT error\n",
24862306a36Sopenharmony_ci				dev->udev->devnum,
24962306a36Sopenharmony_ci				alts->desc.bInterfaceNumber);
25062306a36Sopenharmony_ci			return -EINVAL;
25162306a36Sopenharmony_ci		}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		/* Find the format descriptor from its GUID. */
25462306a36Sopenharmony_ci		fmtdesc = uvc_format_by_guid(&buffer[5]);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci		if (!fmtdesc) {
25762306a36Sopenharmony_ci			/*
25862306a36Sopenharmony_ci			 * Unknown video formats are not fatal errors, the
25962306a36Sopenharmony_ci			 * caller will skip this descriptor.
26062306a36Sopenharmony_ci			 */
26162306a36Sopenharmony_ci			dev_info(&streaming->intf->dev,
26262306a36Sopenharmony_ci				 "Unknown video format %pUl\n", &buffer[5]);
26362306a36Sopenharmony_ci			return 0;
26462306a36Sopenharmony_ci		}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		format->fcc = fmtdesc->fcc;
26762306a36Sopenharmony_ci		format->bpp = buffer[21];
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		/*
27062306a36Sopenharmony_ci		 * Some devices report a format that doesn't match what they
27162306a36Sopenharmony_ci		 * really send.
27262306a36Sopenharmony_ci		 */
27362306a36Sopenharmony_ci		if (dev->quirks & UVC_QUIRK_FORCE_Y8) {
27462306a36Sopenharmony_ci			if (format->fcc == V4L2_PIX_FMT_YUYV) {
27562306a36Sopenharmony_ci				format->fcc = V4L2_PIX_FMT_GREY;
27662306a36Sopenharmony_ci				format->bpp = 8;
27762306a36Sopenharmony_ci				width_multiplier = 2;
27862306a36Sopenharmony_ci			}
27962306a36Sopenharmony_ci		}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		/* Some devices report bpp that doesn't match the format. */
28262306a36Sopenharmony_ci		if (dev->quirks & UVC_QUIRK_FORCE_BPP) {
28362306a36Sopenharmony_ci			const struct v4l2_format_info *info =
28462306a36Sopenharmony_ci				v4l2_format_info(format->fcc);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci			if (info) {
28762306a36Sopenharmony_ci				unsigned int div = info->hdiv * info->vdiv;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci				n = info->bpp[0] * div;
29062306a36Sopenharmony_ci				for (i = 1; i < info->comp_planes; i++)
29162306a36Sopenharmony_ci					n += info->bpp[i];
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci				format->bpp = DIV_ROUND_UP(8 * n, div);
29462306a36Sopenharmony_ci			}
29562306a36Sopenharmony_ci		}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) {
29862306a36Sopenharmony_ci			ftype = UVC_VS_FRAME_UNCOMPRESSED;
29962306a36Sopenharmony_ci		} else {
30062306a36Sopenharmony_ci			ftype = UVC_VS_FRAME_FRAME_BASED;
30162306a36Sopenharmony_ci			if (buffer[27])
30262306a36Sopenharmony_ci				format->flags = UVC_FMT_FLAG_COMPRESSED;
30362306a36Sopenharmony_ci		}
30462306a36Sopenharmony_ci		break;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	case UVC_VS_FORMAT_MJPEG:
30762306a36Sopenharmony_ci		if (buflen < 11) {
30862306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
30962306a36Sopenharmony_ci				"device %d videostreaming interface %d FORMAT error\n",
31062306a36Sopenharmony_ci				dev->udev->devnum,
31162306a36Sopenharmony_ci				alts->desc.bInterfaceNumber);
31262306a36Sopenharmony_ci			return -EINVAL;
31362306a36Sopenharmony_ci		}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci		format->fcc = V4L2_PIX_FMT_MJPEG;
31662306a36Sopenharmony_ci		format->flags = UVC_FMT_FLAG_COMPRESSED;
31762306a36Sopenharmony_ci		format->bpp = 0;
31862306a36Sopenharmony_ci		ftype = UVC_VS_FRAME_MJPEG;
31962306a36Sopenharmony_ci		break;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	case UVC_VS_FORMAT_DV:
32262306a36Sopenharmony_ci		if (buflen < 9) {
32362306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
32462306a36Sopenharmony_ci				"device %d videostreaming interface %d FORMAT error\n",
32562306a36Sopenharmony_ci				dev->udev->devnum,
32662306a36Sopenharmony_ci				alts->desc.bInterfaceNumber);
32762306a36Sopenharmony_ci			return -EINVAL;
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci		if ((buffer[8] & 0x7f) > 2) {
33162306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
33262306a36Sopenharmony_ci				"device %d videostreaming interface %d: unknown DV format %u\n",
33362306a36Sopenharmony_ci				dev->udev->devnum,
33462306a36Sopenharmony_ci				alts->desc.bInterfaceNumber, buffer[8]);
33562306a36Sopenharmony_ci			return -EINVAL;
33662306a36Sopenharmony_ci		}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci		format->fcc = V4L2_PIX_FMT_DV;
33962306a36Sopenharmony_ci		format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM;
34062306a36Sopenharmony_ci		format->bpp = 0;
34162306a36Sopenharmony_ci		ftype = 0;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci		/* Create a dummy frame descriptor. */
34462306a36Sopenharmony_ci		frame = &frames[0];
34562306a36Sopenharmony_ci		memset(frame, 0, sizeof(*frame));
34662306a36Sopenharmony_ci		frame->bFrameIntervalType = 1;
34762306a36Sopenharmony_ci		frame->dwDefaultFrameInterval = 1;
34862306a36Sopenharmony_ci		frame->dwFrameInterval = *intervals;
34962306a36Sopenharmony_ci		*(*intervals)++ = 1;
35062306a36Sopenharmony_ci		format->nframes = 1;
35162306a36Sopenharmony_ci		break;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	case UVC_VS_FORMAT_MPEG2TS:
35462306a36Sopenharmony_ci	case UVC_VS_FORMAT_STREAM_BASED:
35562306a36Sopenharmony_ci		/* Not supported yet. */
35662306a36Sopenharmony_ci	default:
35762306a36Sopenharmony_ci		uvc_dbg(dev, DESCR,
35862306a36Sopenharmony_ci			"device %d videostreaming interface %d unsupported format %u\n",
35962306a36Sopenharmony_ci			dev->udev->devnum, alts->desc.bInterfaceNumber,
36062306a36Sopenharmony_ci			buffer[2]);
36162306a36Sopenharmony_ci		return -EINVAL;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	uvc_dbg(dev, DESCR, "Found format %p4cc", &format->fcc);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	buflen -= buffer[0];
36762306a36Sopenharmony_ci	buffer += buffer[0];
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/*
37062306a36Sopenharmony_ci	 * Parse the frame descriptors. Only uncompressed, MJPEG and frame
37162306a36Sopenharmony_ci	 * based formats have frame descriptors.
37262306a36Sopenharmony_ci	 */
37362306a36Sopenharmony_ci	while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
37462306a36Sopenharmony_ci	       buffer[2] == ftype) {
37562306a36Sopenharmony_ci		unsigned int maxIntervalIndex;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		frame = &frames[format->nframes];
37862306a36Sopenharmony_ci		if (ftype != UVC_VS_FRAME_FRAME_BASED)
37962306a36Sopenharmony_ci			n = buflen > 25 ? buffer[25] : 0;
38062306a36Sopenharmony_ci		else
38162306a36Sopenharmony_ci			n = buflen > 21 ? buffer[21] : 0;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		n = n ? n : 3;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci		if (buflen < 26 + 4*n) {
38662306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
38762306a36Sopenharmony_ci				"device %d videostreaming interface %d FRAME error\n",
38862306a36Sopenharmony_ci				dev->udev->devnum,
38962306a36Sopenharmony_ci				alts->desc.bInterfaceNumber);
39062306a36Sopenharmony_ci			return -EINVAL;
39162306a36Sopenharmony_ci		}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		frame->bFrameIndex = buffer[3];
39462306a36Sopenharmony_ci		frame->bmCapabilities = buffer[4];
39562306a36Sopenharmony_ci		frame->wWidth = get_unaligned_le16(&buffer[5])
39662306a36Sopenharmony_ci			      * width_multiplier;
39762306a36Sopenharmony_ci		frame->wHeight = get_unaligned_le16(&buffer[7]);
39862306a36Sopenharmony_ci		frame->dwMinBitRate = get_unaligned_le32(&buffer[9]);
39962306a36Sopenharmony_ci		frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]);
40062306a36Sopenharmony_ci		if (ftype != UVC_VS_FRAME_FRAME_BASED) {
40162306a36Sopenharmony_ci			frame->dwMaxVideoFrameBufferSize =
40262306a36Sopenharmony_ci				get_unaligned_le32(&buffer[17]);
40362306a36Sopenharmony_ci			frame->dwDefaultFrameInterval =
40462306a36Sopenharmony_ci				get_unaligned_le32(&buffer[21]);
40562306a36Sopenharmony_ci			frame->bFrameIntervalType = buffer[25];
40662306a36Sopenharmony_ci		} else {
40762306a36Sopenharmony_ci			frame->dwMaxVideoFrameBufferSize = 0;
40862306a36Sopenharmony_ci			frame->dwDefaultFrameInterval =
40962306a36Sopenharmony_ci				get_unaligned_le32(&buffer[17]);
41062306a36Sopenharmony_ci			frame->bFrameIntervalType = buffer[21];
41162306a36Sopenharmony_ci		}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci		/*
41462306a36Sopenharmony_ci		 * Copy the frame intervals.
41562306a36Sopenharmony_ci		 *
41662306a36Sopenharmony_ci		 * Some bogus devices report dwMinFrameInterval equal to
41762306a36Sopenharmony_ci		 * dwMaxFrameInterval and have dwFrameIntervalStep set to
41862306a36Sopenharmony_ci		 * zero. Setting all null intervals to 1 fixes the problem and
41962306a36Sopenharmony_ci		 * some other divisions by zero that could happen.
42062306a36Sopenharmony_ci		 */
42162306a36Sopenharmony_ci		frame->dwFrameInterval = *intervals;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci		for (i = 0; i < n; ++i) {
42462306a36Sopenharmony_ci			interval = get_unaligned_le32(&buffer[26+4*i]);
42562306a36Sopenharmony_ci			(*intervals)[i] = interval ? interval : 1;
42662306a36Sopenharmony_ci		}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci		/*
42962306a36Sopenharmony_ci		 * Apply more fixes, quirks and workarounds to handle incorrect
43062306a36Sopenharmony_ci		 * or broken descriptors.
43162306a36Sopenharmony_ci		 */
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci		/*
43462306a36Sopenharmony_ci		 * Several UVC chipsets screw up dwMaxVideoFrameBufferSize
43562306a36Sopenharmony_ci		 * completely. Observed behaviours range from setting the
43662306a36Sopenharmony_ci		 * value to 1.1x the actual frame size to hardwiring the
43762306a36Sopenharmony_ci		 * 16 low bits to 0. This results in a higher than necessary
43862306a36Sopenharmony_ci		 * memory usage as well as a wrong image size information. For
43962306a36Sopenharmony_ci		 * uncompressed formats this can be fixed by computing the
44062306a36Sopenharmony_ci		 * value from the frame size.
44162306a36Sopenharmony_ci		 */
44262306a36Sopenharmony_ci		if (!(format->flags & UVC_FMT_FLAG_COMPRESSED))
44362306a36Sopenharmony_ci			frame->dwMaxVideoFrameBufferSize = format->bpp
44462306a36Sopenharmony_ci				* frame->wWidth * frame->wHeight / 8;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci		/*
44762306a36Sopenharmony_ci		 * Clamp the default frame interval to the boundaries. A zero
44862306a36Sopenharmony_ci		 * bFrameIntervalType value indicates a continuous frame
44962306a36Sopenharmony_ci		 * interval range, with dwFrameInterval[0] storing the minimum
45062306a36Sopenharmony_ci		 * value and dwFrameInterval[1] storing the maximum value.
45162306a36Sopenharmony_ci		 */
45262306a36Sopenharmony_ci		maxIntervalIndex = frame->bFrameIntervalType ? n - 1 : 1;
45362306a36Sopenharmony_ci		frame->dwDefaultFrameInterval =
45462306a36Sopenharmony_ci			clamp(frame->dwDefaultFrameInterval,
45562306a36Sopenharmony_ci			      frame->dwFrameInterval[0],
45662306a36Sopenharmony_ci			      frame->dwFrameInterval[maxIntervalIndex]);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		/*
45962306a36Sopenharmony_ci		 * Some devices report frame intervals that are not functional.
46062306a36Sopenharmony_ci		 * If the corresponding quirk is set, restrict operation to the
46162306a36Sopenharmony_ci		 * first interval only.
46262306a36Sopenharmony_ci		 */
46362306a36Sopenharmony_ci		if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) {
46462306a36Sopenharmony_ci			frame->bFrameIntervalType = 1;
46562306a36Sopenharmony_ci			(*intervals)[0] = frame->dwDefaultFrameInterval;
46662306a36Sopenharmony_ci		}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		uvc_dbg(dev, DESCR, "- %ux%u (%u.%u fps)\n",
46962306a36Sopenharmony_ci			frame->wWidth, frame->wHeight,
47062306a36Sopenharmony_ci			10000000 / frame->dwDefaultFrameInterval,
47162306a36Sopenharmony_ci			(100000000 / frame->dwDefaultFrameInterval) % 10);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci		format->nframes++;
47462306a36Sopenharmony_ci		*intervals += n;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci		buflen -= buffer[0];
47762306a36Sopenharmony_ci		buffer += buffer[0];
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
48162306a36Sopenharmony_ci	    buffer[2] == UVC_VS_STILL_IMAGE_FRAME) {
48262306a36Sopenharmony_ci		buflen -= buffer[0];
48362306a36Sopenharmony_ci		buffer += buffer[0];
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
48762306a36Sopenharmony_ci	    buffer[2] == UVC_VS_COLORFORMAT) {
48862306a36Sopenharmony_ci		if (buflen < 6) {
48962306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
49062306a36Sopenharmony_ci				"device %d videostreaming interface %d COLORFORMAT error\n",
49162306a36Sopenharmony_ci				dev->udev->devnum,
49262306a36Sopenharmony_ci				alts->desc.bInterfaceNumber);
49362306a36Sopenharmony_ci			return -EINVAL;
49462306a36Sopenharmony_ci		}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci		format->colorspace = uvc_colorspace(buffer[3]);
49762306a36Sopenharmony_ci		format->xfer_func = uvc_xfer_func(buffer[4]);
49862306a36Sopenharmony_ci		format->ycbcr_enc = uvc_ycbcr_enc(buffer[5]);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci		buflen -= buffer[0];
50162306a36Sopenharmony_ci		buffer += buffer[0];
50262306a36Sopenharmony_ci	} else {
50362306a36Sopenharmony_ci		format->colorspace = V4L2_COLORSPACE_SRGB;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	return buffer - start;
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic int uvc_parse_streaming(struct uvc_device *dev,
51062306a36Sopenharmony_ci	struct usb_interface *intf)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	struct uvc_streaming *streaming = NULL;
51362306a36Sopenharmony_ci	struct uvc_format *format;
51462306a36Sopenharmony_ci	struct uvc_frame *frame;
51562306a36Sopenharmony_ci	struct usb_host_interface *alts = &intf->altsetting[0];
51662306a36Sopenharmony_ci	const unsigned char *_buffer, *buffer = alts->extra;
51762306a36Sopenharmony_ci	int _buflen, buflen = alts->extralen;
51862306a36Sopenharmony_ci	unsigned int nformats = 0, nframes = 0, nintervals = 0;
51962306a36Sopenharmony_ci	unsigned int size, i, n, p;
52062306a36Sopenharmony_ci	u32 *interval;
52162306a36Sopenharmony_ci	u16 psize;
52262306a36Sopenharmony_ci	int ret = -EINVAL;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if (intf->cur_altsetting->desc.bInterfaceSubClass
52562306a36Sopenharmony_ci		!= UVC_SC_VIDEOSTREAMING) {
52662306a36Sopenharmony_ci		uvc_dbg(dev, DESCR,
52762306a36Sopenharmony_ci			"device %d interface %d isn't a video streaming interface\n",
52862306a36Sopenharmony_ci			dev->udev->devnum,
52962306a36Sopenharmony_ci			intf->altsetting[0].desc.bInterfaceNumber);
53062306a36Sopenharmony_ci		return -EINVAL;
53162306a36Sopenharmony_ci	}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) {
53462306a36Sopenharmony_ci		uvc_dbg(dev, DESCR,
53562306a36Sopenharmony_ci			"device %d interface %d is already claimed\n",
53662306a36Sopenharmony_ci			dev->udev->devnum,
53762306a36Sopenharmony_ci			intf->altsetting[0].desc.bInterfaceNumber);
53862306a36Sopenharmony_ci		return -EINVAL;
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	streaming = uvc_stream_new(dev, intf);
54262306a36Sopenharmony_ci	if (streaming == NULL) {
54362306a36Sopenharmony_ci		usb_driver_release_interface(&uvc_driver.driver, intf);
54462306a36Sopenharmony_ci		return -ENOMEM;
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	/*
54862306a36Sopenharmony_ci	 * The Pico iMage webcam has its class-specific interface descriptors
54962306a36Sopenharmony_ci	 * after the endpoint descriptors.
55062306a36Sopenharmony_ci	 */
55162306a36Sopenharmony_ci	if (buflen == 0) {
55262306a36Sopenharmony_ci		for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
55362306a36Sopenharmony_ci			struct usb_host_endpoint *ep = &alts->endpoint[i];
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci			if (ep->extralen == 0)
55662306a36Sopenharmony_ci				continue;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci			if (ep->extralen > 2 &&
55962306a36Sopenharmony_ci			    ep->extra[1] == USB_DT_CS_INTERFACE) {
56062306a36Sopenharmony_ci				uvc_dbg(dev, DESCR,
56162306a36Sopenharmony_ci					"trying extra data from endpoint %u\n",
56262306a36Sopenharmony_ci					i);
56362306a36Sopenharmony_ci				buffer = alts->endpoint[i].extra;
56462306a36Sopenharmony_ci				buflen = alts->endpoint[i].extralen;
56562306a36Sopenharmony_ci				break;
56662306a36Sopenharmony_ci			}
56762306a36Sopenharmony_ci		}
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	/* Skip the standard interface descriptors. */
57162306a36Sopenharmony_ci	while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) {
57262306a36Sopenharmony_ci		buflen -= buffer[0];
57362306a36Sopenharmony_ci		buffer += buffer[0];
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	if (buflen <= 2) {
57762306a36Sopenharmony_ci		uvc_dbg(dev, DESCR,
57862306a36Sopenharmony_ci			"no class-specific streaming interface descriptors found\n");
57962306a36Sopenharmony_ci		goto error;
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	/* Parse the header descriptor. */
58362306a36Sopenharmony_ci	switch (buffer[2]) {
58462306a36Sopenharmony_ci	case UVC_VS_OUTPUT_HEADER:
58562306a36Sopenharmony_ci		streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
58662306a36Sopenharmony_ci		size = 9;
58762306a36Sopenharmony_ci		break;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	case UVC_VS_INPUT_HEADER:
59062306a36Sopenharmony_ci		streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
59162306a36Sopenharmony_ci		size = 13;
59262306a36Sopenharmony_ci		break;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	default:
59562306a36Sopenharmony_ci		uvc_dbg(dev, DESCR,
59662306a36Sopenharmony_ci			"device %d videostreaming interface %d HEADER descriptor not found\n",
59762306a36Sopenharmony_ci			dev->udev->devnum, alts->desc.bInterfaceNumber);
59862306a36Sopenharmony_ci		goto error;
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	p = buflen >= 4 ? buffer[3] : 0;
60262306a36Sopenharmony_ci	n = buflen >= size ? buffer[size-1] : 0;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	if (buflen < size + p*n) {
60562306a36Sopenharmony_ci		uvc_dbg(dev, DESCR,
60662306a36Sopenharmony_ci			"device %d videostreaming interface %d HEADER descriptor is invalid\n",
60762306a36Sopenharmony_ci			dev->udev->devnum, alts->desc.bInterfaceNumber);
60862306a36Sopenharmony_ci		goto error;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	streaming->header.bNumFormats = p;
61262306a36Sopenharmony_ci	streaming->header.bEndpointAddress = buffer[6];
61362306a36Sopenharmony_ci	if (buffer[2] == UVC_VS_INPUT_HEADER) {
61462306a36Sopenharmony_ci		streaming->header.bmInfo = buffer[7];
61562306a36Sopenharmony_ci		streaming->header.bTerminalLink = buffer[8];
61662306a36Sopenharmony_ci		streaming->header.bStillCaptureMethod = buffer[9];
61762306a36Sopenharmony_ci		streaming->header.bTriggerSupport = buffer[10];
61862306a36Sopenharmony_ci		streaming->header.bTriggerUsage = buffer[11];
61962306a36Sopenharmony_ci	} else {
62062306a36Sopenharmony_ci		streaming->header.bTerminalLink = buffer[7];
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci	streaming->header.bControlSize = n;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	streaming->header.bmaControls = kmemdup(&buffer[size], p * n,
62562306a36Sopenharmony_ci						GFP_KERNEL);
62662306a36Sopenharmony_ci	if (streaming->header.bmaControls == NULL) {
62762306a36Sopenharmony_ci		ret = -ENOMEM;
62862306a36Sopenharmony_ci		goto error;
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	buflen -= buffer[0];
63262306a36Sopenharmony_ci	buffer += buffer[0];
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	_buffer = buffer;
63562306a36Sopenharmony_ci	_buflen = buflen;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	/* Count the format and frame descriptors. */
63862306a36Sopenharmony_ci	while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) {
63962306a36Sopenharmony_ci		switch (_buffer[2]) {
64062306a36Sopenharmony_ci		case UVC_VS_FORMAT_UNCOMPRESSED:
64162306a36Sopenharmony_ci		case UVC_VS_FORMAT_MJPEG:
64262306a36Sopenharmony_ci		case UVC_VS_FORMAT_FRAME_BASED:
64362306a36Sopenharmony_ci			nformats++;
64462306a36Sopenharmony_ci			break;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci		case UVC_VS_FORMAT_DV:
64762306a36Sopenharmony_ci			/*
64862306a36Sopenharmony_ci			 * DV format has no frame descriptor. We will create a
64962306a36Sopenharmony_ci			 * dummy frame descriptor with a dummy frame interval.
65062306a36Sopenharmony_ci			 */
65162306a36Sopenharmony_ci			nformats++;
65262306a36Sopenharmony_ci			nframes++;
65362306a36Sopenharmony_ci			nintervals++;
65462306a36Sopenharmony_ci			break;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci		case UVC_VS_FORMAT_MPEG2TS:
65762306a36Sopenharmony_ci		case UVC_VS_FORMAT_STREAM_BASED:
65862306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
65962306a36Sopenharmony_ci				"device %d videostreaming interface %d FORMAT %u is not supported\n",
66062306a36Sopenharmony_ci				dev->udev->devnum,
66162306a36Sopenharmony_ci				alts->desc.bInterfaceNumber, _buffer[2]);
66262306a36Sopenharmony_ci			break;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci		case UVC_VS_FRAME_UNCOMPRESSED:
66562306a36Sopenharmony_ci		case UVC_VS_FRAME_MJPEG:
66662306a36Sopenharmony_ci			nframes++;
66762306a36Sopenharmony_ci			if (_buflen > 25)
66862306a36Sopenharmony_ci				nintervals += _buffer[25] ? _buffer[25] : 3;
66962306a36Sopenharmony_ci			break;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci		case UVC_VS_FRAME_FRAME_BASED:
67262306a36Sopenharmony_ci			nframes++;
67362306a36Sopenharmony_ci			if (_buflen > 21)
67462306a36Sopenharmony_ci				nintervals += _buffer[21] ? _buffer[21] : 3;
67562306a36Sopenharmony_ci			break;
67662306a36Sopenharmony_ci		}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci		_buflen -= _buffer[0];
67962306a36Sopenharmony_ci		_buffer += _buffer[0];
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	if (nformats == 0) {
68362306a36Sopenharmony_ci		uvc_dbg(dev, DESCR,
68462306a36Sopenharmony_ci			"device %d videostreaming interface %d has no supported formats defined\n",
68562306a36Sopenharmony_ci			dev->udev->devnum, alts->desc.bInterfaceNumber);
68662306a36Sopenharmony_ci		goto error;
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	size = nformats * sizeof(*format) + nframes * sizeof(*frame)
69062306a36Sopenharmony_ci	     + nintervals * sizeof(*interval);
69162306a36Sopenharmony_ci	format = kzalloc(size, GFP_KERNEL);
69262306a36Sopenharmony_ci	if (format == NULL) {
69362306a36Sopenharmony_ci		ret = -ENOMEM;
69462306a36Sopenharmony_ci		goto error;
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	frame = (struct uvc_frame *)&format[nformats];
69862306a36Sopenharmony_ci	interval = (u32 *)&frame[nframes];
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	streaming->formats = format;
70162306a36Sopenharmony_ci	streaming->nformats = 0;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	/* Parse the format descriptors. */
70462306a36Sopenharmony_ci	while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) {
70562306a36Sopenharmony_ci		switch (buffer[2]) {
70662306a36Sopenharmony_ci		case UVC_VS_FORMAT_UNCOMPRESSED:
70762306a36Sopenharmony_ci		case UVC_VS_FORMAT_MJPEG:
70862306a36Sopenharmony_ci		case UVC_VS_FORMAT_DV:
70962306a36Sopenharmony_ci		case UVC_VS_FORMAT_FRAME_BASED:
71062306a36Sopenharmony_ci			ret = uvc_parse_format(dev, streaming, format, frame,
71162306a36Sopenharmony_ci				&interval, buffer, buflen);
71262306a36Sopenharmony_ci			if (ret < 0)
71362306a36Sopenharmony_ci				goto error;
71462306a36Sopenharmony_ci			if (!ret)
71562306a36Sopenharmony_ci				break;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci			streaming->nformats++;
71862306a36Sopenharmony_ci			frame += format->nframes;
71962306a36Sopenharmony_ci			format++;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci			buflen -= ret;
72262306a36Sopenharmony_ci			buffer += ret;
72362306a36Sopenharmony_ci			continue;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci		default:
72662306a36Sopenharmony_ci			break;
72762306a36Sopenharmony_ci		}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci		buflen -= buffer[0];
73062306a36Sopenharmony_ci		buffer += buffer[0];
73162306a36Sopenharmony_ci	}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	if (buflen)
73462306a36Sopenharmony_ci		uvc_dbg(dev, DESCR,
73562306a36Sopenharmony_ci			"device %d videostreaming interface %d has %u bytes of trailing descriptor garbage\n",
73662306a36Sopenharmony_ci			dev->udev->devnum, alts->desc.bInterfaceNumber, buflen);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	/* Parse the alternate settings to find the maximum bandwidth. */
73962306a36Sopenharmony_ci	for (i = 0; i < intf->num_altsetting; ++i) {
74062306a36Sopenharmony_ci		struct usb_host_endpoint *ep;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci		alts = &intf->altsetting[i];
74362306a36Sopenharmony_ci		ep = uvc_find_endpoint(alts,
74462306a36Sopenharmony_ci				streaming->header.bEndpointAddress);
74562306a36Sopenharmony_ci		if (ep == NULL)
74662306a36Sopenharmony_ci			continue;
74762306a36Sopenharmony_ci		psize = uvc_endpoint_max_bpi(dev->udev, ep);
74862306a36Sopenharmony_ci		if (psize > streaming->maxpsize)
74962306a36Sopenharmony_ci			streaming->maxpsize = psize;
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	list_add_tail(&streaming->list, &dev->streams);
75362306a36Sopenharmony_ci	return 0;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_cierror:
75662306a36Sopenharmony_ci	usb_driver_release_interface(&uvc_driver.driver, intf);
75762306a36Sopenharmony_ci	uvc_stream_delete(streaming);
75862306a36Sopenharmony_ci	return ret;
75962306a36Sopenharmony_ci}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_cistatic const u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA;
76262306a36Sopenharmony_cistatic const u8 uvc_gpio_guid[16] = UVC_GUID_EXT_GPIO_CONTROLLER;
76362306a36Sopenharmony_cistatic const u8 uvc_media_transport_input_guid[16] =
76462306a36Sopenharmony_ci	UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT;
76562306a36Sopenharmony_cistatic const u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cistatic struct uvc_entity *uvc_alloc_entity(u16 type, u16 id,
76862306a36Sopenharmony_ci		unsigned int num_pads, unsigned int extra_size)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	struct uvc_entity *entity;
77162306a36Sopenharmony_ci	unsigned int num_inputs;
77262306a36Sopenharmony_ci	unsigned int size;
77362306a36Sopenharmony_ci	unsigned int i;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	extra_size = roundup(extra_size, sizeof(*entity->pads));
77662306a36Sopenharmony_ci	if (num_pads)
77762306a36Sopenharmony_ci		num_inputs = type & UVC_TERM_OUTPUT ? num_pads : num_pads - 1;
77862306a36Sopenharmony_ci	else
77962306a36Sopenharmony_ci		num_inputs = 0;
78062306a36Sopenharmony_ci	size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads
78162306a36Sopenharmony_ci	     + num_inputs;
78262306a36Sopenharmony_ci	entity = kzalloc(size, GFP_KERNEL);
78362306a36Sopenharmony_ci	if (entity == NULL)
78462306a36Sopenharmony_ci		return NULL;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	entity->id = id;
78762306a36Sopenharmony_ci	entity->type = type;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	/*
79062306a36Sopenharmony_ci	 * Set the GUID for standard entity types. For extension units, the GUID
79162306a36Sopenharmony_ci	 * is initialized by the caller.
79262306a36Sopenharmony_ci	 */
79362306a36Sopenharmony_ci	switch (type) {
79462306a36Sopenharmony_ci	case UVC_EXT_GPIO_UNIT:
79562306a36Sopenharmony_ci		memcpy(entity->guid, uvc_gpio_guid, 16);
79662306a36Sopenharmony_ci		break;
79762306a36Sopenharmony_ci	case UVC_ITT_CAMERA:
79862306a36Sopenharmony_ci		memcpy(entity->guid, uvc_camera_guid, 16);
79962306a36Sopenharmony_ci		break;
80062306a36Sopenharmony_ci	case UVC_ITT_MEDIA_TRANSPORT_INPUT:
80162306a36Sopenharmony_ci		memcpy(entity->guid, uvc_media_transport_input_guid, 16);
80262306a36Sopenharmony_ci		break;
80362306a36Sopenharmony_ci	case UVC_VC_PROCESSING_UNIT:
80462306a36Sopenharmony_ci		memcpy(entity->guid, uvc_processing_guid, 16);
80562306a36Sopenharmony_ci		break;
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	entity->num_links = 0;
80962306a36Sopenharmony_ci	entity->num_pads = num_pads;
81062306a36Sopenharmony_ci	entity->pads = ((void *)(entity + 1)) + extra_size;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	for (i = 0; i < num_inputs; ++i)
81362306a36Sopenharmony_ci		entity->pads[i].flags = MEDIA_PAD_FL_SINK;
81462306a36Sopenharmony_ci	if (!UVC_ENTITY_IS_OTERM(entity) && num_pads)
81562306a36Sopenharmony_ci		entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	entity->bNrInPins = num_inputs;
81862306a36Sopenharmony_ci	entity->baSourceID = (u8 *)(&entity->pads[num_pads]);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	return entity;
82162306a36Sopenharmony_ci}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_cistatic void uvc_entity_set_name(struct uvc_device *dev, struct uvc_entity *entity,
82462306a36Sopenharmony_ci				const char *type_name, u8 string_id)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	int ret;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	/*
82962306a36Sopenharmony_ci	 * First attempt to read the entity name from the device. If the entity
83062306a36Sopenharmony_ci	 * has no associated string, or if reading the string fails (most
83162306a36Sopenharmony_ci	 * likely due to a buggy firmware), fall back to default names based on
83262306a36Sopenharmony_ci	 * the entity type.
83362306a36Sopenharmony_ci	 */
83462306a36Sopenharmony_ci	if (string_id) {
83562306a36Sopenharmony_ci		ret = usb_string(dev->udev, string_id, entity->name,
83662306a36Sopenharmony_ci				 sizeof(entity->name));
83762306a36Sopenharmony_ci		if (!ret)
83862306a36Sopenharmony_ci			return;
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	sprintf(entity->name, "%s %u", type_name, entity->id);
84262306a36Sopenharmony_ci}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci/* Parse vendor-specific extensions. */
84562306a36Sopenharmony_cistatic int uvc_parse_vendor_control(struct uvc_device *dev,
84662306a36Sopenharmony_ci	const unsigned char *buffer, int buflen)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	struct usb_device *udev = dev->udev;
84962306a36Sopenharmony_ci	struct usb_host_interface *alts = dev->intf->cur_altsetting;
85062306a36Sopenharmony_ci	struct uvc_entity *unit;
85162306a36Sopenharmony_ci	unsigned int n, p;
85262306a36Sopenharmony_ci	int handled = 0;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	switch (le16_to_cpu(dev->udev->descriptor.idVendor)) {
85562306a36Sopenharmony_ci	case 0x046d:		/* Logitech */
85662306a36Sopenharmony_ci		if (buffer[1] != 0x41 || buffer[2] != 0x01)
85762306a36Sopenharmony_ci			break;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci		/*
86062306a36Sopenharmony_ci		 * Logitech implements several vendor specific functions
86162306a36Sopenharmony_ci		 * through vendor specific extension units (LXU).
86262306a36Sopenharmony_ci		 *
86362306a36Sopenharmony_ci		 * The LXU descriptors are similar to XU descriptors
86462306a36Sopenharmony_ci		 * (see "USB Device Video Class for Video Devices", section
86562306a36Sopenharmony_ci		 * 3.7.2.6 "Extension Unit Descriptor") with the following
86662306a36Sopenharmony_ci		 * differences:
86762306a36Sopenharmony_ci		 *
86862306a36Sopenharmony_ci		 * ----------------------------------------------------------
86962306a36Sopenharmony_ci		 * 0		bLength		1	 Number
87062306a36Sopenharmony_ci		 *	Size of this descriptor, in bytes: 24+p+n*2
87162306a36Sopenharmony_ci		 * ----------------------------------------------------------
87262306a36Sopenharmony_ci		 * 23+p+n	bmControlsType	N	Bitmap
87362306a36Sopenharmony_ci		 *	Individual bits in the set are defined:
87462306a36Sopenharmony_ci		 *	0: Absolute
87562306a36Sopenharmony_ci		 *	1: Relative
87662306a36Sopenharmony_ci		 *
87762306a36Sopenharmony_ci		 *	This bitset is mapped exactly the same as bmControls.
87862306a36Sopenharmony_ci		 * ----------------------------------------------------------
87962306a36Sopenharmony_ci		 * 23+p+n*2	bReserved	1	Boolean
88062306a36Sopenharmony_ci		 * ----------------------------------------------------------
88162306a36Sopenharmony_ci		 * 24+p+n*2	iExtension	1	Index
88262306a36Sopenharmony_ci		 *	Index of a string descriptor that describes this
88362306a36Sopenharmony_ci		 *	extension unit.
88462306a36Sopenharmony_ci		 * ----------------------------------------------------------
88562306a36Sopenharmony_ci		 */
88662306a36Sopenharmony_ci		p = buflen >= 22 ? buffer[21] : 0;
88762306a36Sopenharmony_ci		n = buflen >= 25 + p ? buffer[22+p] : 0;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci		if (buflen < 25 + p + 2*n) {
89062306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
89162306a36Sopenharmony_ci				"device %d videocontrol interface %d EXTENSION_UNIT error\n",
89262306a36Sopenharmony_ci				udev->devnum, alts->desc.bInterfaceNumber);
89362306a36Sopenharmony_ci			break;
89462306a36Sopenharmony_ci		}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci		unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3],
89762306a36Sopenharmony_ci					p + 1, 2*n);
89862306a36Sopenharmony_ci		if (unit == NULL)
89962306a36Sopenharmony_ci			return -ENOMEM;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci		memcpy(unit->guid, &buffer[4], 16);
90262306a36Sopenharmony_ci		unit->extension.bNumControls = buffer[20];
90362306a36Sopenharmony_ci		memcpy(unit->baSourceID, &buffer[22], p);
90462306a36Sopenharmony_ci		unit->extension.bControlSize = buffer[22+p];
90562306a36Sopenharmony_ci		unit->extension.bmControls = (u8 *)unit + sizeof(*unit);
90662306a36Sopenharmony_ci		unit->extension.bmControlsType = (u8 *)unit + sizeof(*unit)
90762306a36Sopenharmony_ci					       + n;
90862306a36Sopenharmony_ci		memcpy(unit->extension.bmControls, &buffer[23+p], 2*n);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci		uvc_entity_set_name(dev, unit, "Extension", buffer[24+p+2*n]);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci		list_add_tail(&unit->list, &dev->entities);
91362306a36Sopenharmony_ci		handled = 1;
91462306a36Sopenharmony_ci		break;
91562306a36Sopenharmony_ci	}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	return handled;
91862306a36Sopenharmony_ci}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_cistatic int uvc_parse_standard_control(struct uvc_device *dev,
92162306a36Sopenharmony_ci	const unsigned char *buffer, int buflen)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	struct usb_device *udev = dev->udev;
92462306a36Sopenharmony_ci	struct uvc_entity *unit, *term;
92562306a36Sopenharmony_ci	struct usb_interface *intf;
92662306a36Sopenharmony_ci	struct usb_host_interface *alts = dev->intf->cur_altsetting;
92762306a36Sopenharmony_ci	unsigned int i, n, p, len;
92862306a36Sopenharmony_ci	const char *type_name;
92962306a36Sopenharmony_ci	u16 type;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	switch (buffer[2]) {
93262306a36Sopenharmony_ci	case UVC_VC_HEADER:
93362306a36Sopenharmony_ci		n = buflen >= 12 ? buffer[11] : 0;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci		if (buflen < 12 + n) {
93662306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
93762306a36Sopenharmony_ci				"device %d videocontrol interface %d HEADER error\n",
93862306a36Sopenharmony_ci				udev->devnum, alts->desc.bInterfaceNumber);
93962306a36Sopenharmony_ci			return -EINVAL;
94062306a36Sopenharmony_ci		}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci		dev->uvc_version = get_unaligned_le16(&buffer[3]);
94362306a36Sopenharmony_ci		dev->clock_frequency = get_unaligned_le32(&buffer[7]);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci		/* Parse all USB Video Streaming interfaces. */
94662306a36Sopenharmony_ci		for (i = 0; i < n; ++i) {
94762306a36Sopenharmony_ci			intf = usb_ifnum_to_if(udev, buffer[12+i]);
94862306a36Sopenharmony_ci			if (intf == NULL) {
94962306a36Sopenharmony_ci				uvc_dbg(dev, DESCR,
95062306a36Sopenharmony_ci					"device %d interface %d doesn't exists\n",
95162306a36Sopenharmony_ci					udev->devnum, i);
95262306a36Sopenharmony_ci				continue;
95362306a36Sopenharmony_ci			}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci			uvc_parse_streaming(dev, intf);
95662306a36Sopenharmony_ci		}
95762306a36Sopenharmony_ci		break;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	case UVC_VC_INPUT_TERMINAL:
96062306a36Sopenharmony_ci		if (buflen < 8) {
96162306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
96262306a36Sopenharmony_ci				"device %d videocontrol interface %d INPUT_TERMINAL error\n",
96362306a36Sopenharmony_ci				udev->devnum, alts->desc.bInterfaceNumber);
96462306a36Sopenharmony_ci			return -EINVAL;
96562306a36Sopenharmony_ci		}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci		/*
96862306a36Sopenharmony_ci		 * Reject invalid terminal types that would cause issues:
96962306a36Sopenharmony_ci		 *
97062306a36Sopenharmony_ci		 * - The high byte must be non-zero, otherwise it would be
97162306a36Sopenharmony_ci		 *   confused with a unit.
97262306a36Sopenharmony_ci		 *
97362306a36Sopenharmony_ci		 * - Bit 15 must be 0, as we use it internally as a terminal
97462306a36Sopenharmony_ci		 *   direction flag.
97562306a36Sopenharmony_ci		 *
97662306a36Sopenharmony_ci		 * Other unknown types are accepted.
97762306a36Sopenharmony_ci		 */
97862306a36Sopenharmony_ci		type = get_unaligned_le16(&buffer[4]);
97962306a36Sopenharmony_ci		if ((type & 0x7f00) == 0 || (type & 0x8000) != 0) {
98062306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
98162306a36Sopenharmony_ci				"device %d videocontrol interface %d INPUT_TERMINAL %d has invalid type 0x%04x, skipping\n",
98262306a36Sopenharmony_ci				udev->devnum, alts->desc.bInterfaceNumber,
98362306a36Sopenharmony_ci				buffer[3], type);
98462306a36Sopenharmony_ci			return 0;
98562306a36Sopenharmony_ci		}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci		n = 0;
98862306a36Sopenharmony_ci		p = 0;
98962306a36Sopenharmony_ci		len = 8;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci		if (type == UVC_ITT_CAMERA) {
99262306a36Sopenharmony_ci			n = buflen >= 15 ? buffer[14] : 0;
99362306a36Sopenharmony_ci			len = 15;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci		} else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) {
99662306a36Sopenharmony_ci			n = buflen >= 9 ? buffer[8] : 0;
99762306a36Sopenharmony_ci			p = buflen >= 10 + n ? buffer[9+n] : 0;
99862306a36Sopenharmony_ci			len = 10;
99962306a36Sopenharmony_ci		}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci		if (buflen < len + n + p) {
100262306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
100362306a36Sopenharmony_ci				"device %d videocontrol interface %d INPUT_TERMINAL error\n",
100462306a36Sopenharmony_ci				udev->devnum, alts->desc.bInterfaceNumber);
100562306a36Sopenharmony_ci			return -EINVAL;
100662306a36Sopenharmony_ci		}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci		term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3],
100962306a36Sopenharmony_ci					1, n + p);
101062306a36Sopenharmony_ci		if (term == NULL)
101162306a36Sopenharmony_ci			return -ENOMEM;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci		if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) {
101462306a36Sopenharmony_ci			term->camera.bControlSize = n;
101562306a36Sopenharmony_ci			term->camera.bmControls = (u8 *)term + sizeof(*term);
101662306a36Sopenharmony_ci			term->camera.wObjectiveFocalLengthMin =
101762306a36Sopenharmony_ci				get_unaligned_le16(&buffer[8]);
101862306a36Sopenharmony_ci			term->camera.wObjectiveFocalLengthMax =
101962306a36Sopenharmony_ci				get_unaligned_le16(&buffer[10]);
102062306a36Sopenharmony_ci			term->camera.wOcularFocalLength =
102162306a36Sopenharmony_ci				get_unaligned_le16(&buffer[12]);
102262306a36Sopenharmony_ci			memcpy(term->camera.bmControls, &buffer[15], n);
102362306a36Sopenharmony_ci		} else if (UVC_ENTITY_TYPE(term) ==
102462306a36Sopenharmony_ci			   UVC_ITT_MEDIA_TRANSPORT_INPUT) {
102562306a36Sopenharmony_ci			term->media.bControlSize = n;
102662306a36Sopenharmony_ci			term->media.bmControls = (u8 *)term + sizeof(*term);
102762306a36Sopenharmony_ci			term->media.bTransportModeSize = p;
102862306a36Sopenharmony_ci			term->media.bmTransportModes = (u8 *)term
102962306a36Sopenharmony_ci						     + sizeof(*term) + n;
103062306a36Sopenharmony_ci			memcpy(term->media.bmControls, &buffer[9], n);
103162306a36Sopenharmony_ci			memcpy(term->media.bmTransportModes, &buffer[10+n], p);
103262306a36Sopenharmony_ci		}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci		if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA)
103562306a36Sopenharmony_ci			type_name = "Camera";
103662306a36Sopenharmony_ci		else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT)
103762306a36Sopenharmony_ci			type_name = "Media";
103862306a36Sopenharmony_ci		else
103962306a36Sopenharmony_ci			type_name = "Input";
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci		uvc_entity_set_name(dev, term, type_name, buffer[7]);
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci		list_add_tail(&term->list, &dev->entities);
104462306a36Sopenharmony_ci		break;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	case UVC_VC_OUTPUT_TERMINAL:
104762306a36Sopenharmony_ci		if (buflen < 9) {
104862306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
104962306a36Sopenharmony_ci				"device %d videocontrol interface %d OUTPUT_TERMINAL error\n",
105062306a36Sopenharmony_ci				udev->devnum, alts->desc.bInterfaceNumber);
105162306a36Sopenharmony_ci			return -EINVAL;
105262306a36Sopenharmony_ci		}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci		/*
105562306a36Sopenharmony_ci		 * Make sure the terminal type MSB is not null, otherwise it
105662306a36Sopenharmony_ci		 * could be confused with a unit.
105762306a36Sopenharmony_ci		 */
105862306a36Sopenharmony_ci		type = get_unaligned_le16(&buffer[4]);
105962306a36Sopenharmony_ci		if ((type & 0xff00) == 0) {
106062306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
106162306a36Sopenharmony_ci				"device %d videocontrol interface %d OUTPUT_TERMINAL %d has invalid type 0x%04x, skipping\n",
106262306a36Sopenharmony_ci				udev->devnum, alts->desc.bInterfaceNumber,
106362306a36Sopenharmony_ci				buffer[3], type);
106462306a36Sopenharmony_ci			return 0;
106562306a36Sopenharmony_ci		}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci		term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3],
106862306a36Sopenharmony_ci					1, 0);
106962306a36Sopenharmony_ci		if (term == NULL)
107062306a36Sopenharmony_ci			return -ENOMEM;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci		memcpy(term->baSourceID, &buffer[7], 1);
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci		uvc_entity_set_name(dev, term, "Output", buffer[8]);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci		list_add_tail(&term->list, &dev->entities);
107762306a36Sopenharmony_ci		break;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	case UVC_VC_SELECTOR_UNIT:
108062306a36Sopenharmony_ci		p = buflen >= 5 ? buffer[4] : 0;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci		if (buflen < 5 || buflen < 6 + p) {
108362306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
108462306a36Sopenharmony_ci				"device %d videocontrol interface %d SELECTOR_UNIT error\n",
108562306a36Sopenharmony_ci				udev->devnum, alts->desc.bInterfaceNumber);
108662306a36Sopenharmony_ci			return -EINVAL;
108762306a36Sopenharmony_ci		}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci		unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0);
109062306a36Sopenharmony_ci		if (unit == NULL)
109162306a36Sopenharmony_ci			return -ENOMEM;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci		memcpy(unit->baSourceID, &buffer[5], p);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci		uvc_entity_set_name(dev, unit, "Selector", buffer[5+p]);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci		list_add_tail(&unit->list, &dev->entities);
109862306a36Sopenharmony_ci		break;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	case UVC_VC_PROCESSING_UNIT:
110162306a36Sopenharmony_ci		n = buflen >= 8 ? buffer[7] : 0;
110262306a36Sopenharmony_ci		p = dev->uvc_version >= 0x0110 ? 10 : 9;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci		if (buflen < p + n) {
110562306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
110662306a36Sopenharmony_ci				"device %d videocontrol interface %d PROCESSING_UNIT error\n",
110762306a36Sopenharmony_ci				udev->devnum, alts->desc.bInterfaceNumber);
110862306a36Sopenharmony_ci			return -EINVAL;
110962306a36Sopenharmony_ci		}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci		unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n);
111262306a36Sopenharmony_ci		if (unit == NULL)
111362306a36Sopenharmony_ci			return -ENOMEM;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci		memcpy(unit->baSourceID, &buffer[4], 1);
111662306a36Sopenharmony_ci		unit->processing.wMaxMultiplier =
111762306a36Sopenharmony_ci			get_unaligned_le16(&buffer[5]);
111862306a36Sopenharmony_ci		unit->processing.bControlSize = buffer[7];
111962306a36Sopenharmony_ci		unit->processing.bmControls = (u8 *)unit + sizeof(*unit);
112062306a36Sopenharmony_ci		memcpy(unit->processing.bmControls, &buffer[8], n);
112162306a36Sopenharmony_ci		if (dev->uvc_version >= 0x0110)
112262306a36Sopenharmony_ci			unit->processing.bmVideoStandards = buffer[9+n];
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci		uvc_entity_set_name(dev, unit, "Processing", buffer[8+n]);
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci		list_add_tail(&unit->list, &dev->entities);
112762306a36Sopenharmony_ci		break;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	case UVC_VC_EXTENSION_UNIT:
113062306a36Sopenharmony_ci		p = buflen >= 22 ? buffer[21] : 0;
113162306a36Sopenharmony_ci		n = buflen >= 24 + p ? buffer[22+p] : 0;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci		if (buflen < 24 + p + n) {
113462306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
113562306a36Sopenharmony_ci				"device %d videocontrol interface %d EXTENSION_UNIT error\n",
113662306a36Sopenharmony_ci				udev->devnum, alts->desc.bInterfaceNumber);
113762306a36Sopenharmony_ci			return -EINVAL;
113862306a36Sopenharmony_ci		}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci		unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n);
114162306a36Sopenharmony_ci		if (unit == NULL)
114262306a36Sopenharmony_ci			return -ENOMEM;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci		memcpy(unit->guid, &buffer[4], 16);
114562306a36Sopenharmony_ci		unit->extension.bNumControls = buffer[20];
114662306a36Sopenharmony_ci		memcpy(unit->baSourceID, &buffer[22], p);
114762306a36Sopenharmony_ci		unit->extension.bControlSize = buffer[22+p];
114862306a36Sopenharmony_ci		unit->extension.bmControls = (u8 *)unit + sizeof(*unit);
114962306a36Sopenharmony_ci		memcpy(unit->extension.bmControls, &buffer[23+p], n);
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci		uvc_entity_set_name(dev, unit, "Extension", buffer[23+p+n]);
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci		list_add_tail(&unit->list, &dev->entities);
115462306a36Sopenharmony_ci		break;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	default:
115762306a36Sopenharmony_ci		uvc_dbg(dev, DESCR,
115862306a36Sopenharmony_ci			"Found an unknown CS_INTERFACE descriptor (%u)\n",
115962306a36Sopenharmony_ci			buffer[2]);
116062306a36Sopenharmony_ci		break;
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	return 0;
116462306a36Sopenharmony_ci}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_cistatic int uvc_parse_control(struct uvc_device *dev)
116762306a36Sopenharmony_ci{
116862306a36Sopenharmony_ci	struct usb_host_interface *alts = dev->intf->cur_altsetting;
116962306a36Sopenharmony_ci	const unsigned char *buffer = alts->extra;
117062306a36Sopenharmony_ci	int buflen = alts->extralen;
117162306a36Sopenharmony_ci	int ret;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	/*
117462306a36Sopenharmony_ci	 * Parse the default alternate setting only, as the UVC specification
117562306a36Sopenharmony_ci	 * defines a single alternate setting, the default alternate setting
117662306a36Sopenharmony_ci	 * zero.
117762306a36Sopenharmony_ci	 */
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	while (buflen > 2) {
118062306a36Sopenharmony_ci		if (uvc_parse_vendor_control(dev, buffer, buflen) ||
118162306a36Sopenharmony_ci		    buffer[1] != USB_DT_CS_INTERFACE)
118262306a36Sopenharmony_ci			goto next_descriptor;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci		ret = uvc_parse_standard_control(dev, buffer, buflen);
118562306a36Sopenharmony_ci		if (ret < 0)
118662306a36Sopenharmony_ci			return ret;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_cinext_descriptor:
118962306a36Sopenharmony_ci		buflen -= buffer[0];
119062306a36Sopenharmony_ci		buffer += buffer[0];
119162306a36Sopenharmony_ci	}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	/*
119462306a36Sopenharmony_ci	 * Check if the optional status endpoint is present. Built-in iSight
119562306a36Sopenharmony_ci	 * webcams have an interrupt endpoint but spit proprietary data that
119662306a36Sopenharmony_ci	 * don't conform to the UVC status endpoint messages. Don't try to
119762306a36Sopenharmony_ci	 * handle the interrupt endpoint for those cameras.
119862306a36Sopenharmony_ci	 */
119962306a36Sopenharmony_ci	if (alts->desc.bNumEndpoints == 1 &&
120062306a36Sopenharmony_ci	    !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) {
120162306a36Sopenharmony_ci		struct usb_host_endpoint *ep = &alts->endpoint[0];
120262306a36Sopenharmony_ci		struct usb_endpoint_descriptor *desc = &ep->desc;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci		if (usb_endpoint_is_int_in(desc) &&
120562306a36Sopenharmony_ci		    le16_to_cpu(desc->wMaxPacketSize) >= 8 &&
120662306a36Sopenharmony_ci		    desc->bInterval != 0) {
120762306a36Sopenharmony_ci			uvc_dbg(dev, DESCR,
120862306a36Sopenharmony_ci				"Found a Status endpoint (addr %02x)\n",
120962306a36Sopenharmony_ci				desc->bEndpointAddress);
121062306a36Sopenharmony_ci			dev->int_ep = ep;
121162306a36Sopenharmony_ci		}
121262306a36Sopenharmony_ci	}
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	return 0;
121562306a36Sopenharmony_ci}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
121862306a36Sopenharmony_ci * Privacy GPIO
121962306a36Sopenharmony_ci */
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_cistatic void uvc_gpio_event(struct uvc_device *dev)
122262306a36Sopenharmony_ci{
122362306a36Sopenharmony_ci	struct uvc_entity *unit = dev->gpio_unit;
122462306a36Sopenharmony_ci	struct uvc_video_chain *chain;
122562306a36Sopenharmony_ci	u8 new_val;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	if (!unit)
122862306a36Sopenharmony_ci		return;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	new_val = gpiod_get_value_cansleep(unit->gpio.gpio_privacy);
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	/* GPIO entities are always on the first chain. */
123362306a36Sopenharmony_ci	chain = list_first_entry(&dev->chains, struct uvc_video_chain, list);
123462306a36Sopenharmony_ci	uvc_ctrl_status_event(chain, unit->controls, &new_val);
123562306a36Sopenharmony_ci}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_cistatic int uvc_gpio_get_cur(struct uvc_device *dev, struct uvc_entity *entity,
123862306a36Sopenharmony_ci			    u8 cs, void *data, u16 size)
123962306a36Sopenharmony_ci{
124062306a36Sopenharmony_ci	if (cs != UVC_CT_PRIVACY_CONTROL || size < 1)
124162306a36Sopenharmony_ci		return -EINVAL;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	*(u8 *)data = gpiod_get_value_cansleep(entity->gpio.gpio_privacy);
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	return 0;
124662306a36Sopenharmony_ci}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_cistatic int uvc_gpio_get_info(struct uvc_device *dev, struct uvc_entity *entity,
124962306a36Sopenharmony_ci			     u8 cs, u8 *caps)
125062306a36Sopenharmony_ci{
125162306a36Sopenharmony_ci	if (cs != UVC_CT_PRIVACY_CONTROL)
125262306a36Sopenharmony_ci		return -EINVAL;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	*caps = UVC_CONTROL_CAP_GET | UVC_CONTROL_CAP_AUTOUPDATE;
125562306a36Sopenharmony_ci	return 0;
125662306a36Sopenharmony_ci}
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_cistatic irqreturn_t uvc_gpio_irq(int irq, void *data)
125962306a36Sopenharmony_ci{
126062306a36Sopenharmony_ci	struct uvc_device *dev = data;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	uvc_gpio_event(dev);
126362306a36Sopenharmony_ci	return IRQ_HANDLED;
126462306a36Sopenharmony_ci}
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_cistatic int uvc_gpio_parse(struct uvc_device *dev)
126762306a36Sopenharmony_ci{
126862306a36Sopenharmony_ci	struct uvc_entity *unit;
126962306a36Sopenharmony_ci	struct gpio_desc *gpio_privacy;
127062306a36Sopenharmony_ci	int irq;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	gpio_privacy = devm_gpiod_get_optional(&dev->udev->dev, "privacy",
127362306a36Sopenharmony_ci					       GPIOD_IN);
127462306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(gpio_privacy))
127562306a36Sopenharmony_ci		return PTR_ERR_OR_ZERO(gpio_privacy);
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	irq = gpiod_to_irq(gpio_privacy);
127862306a36Sopenharmony_ci	if (irq < 0)
127962306a36Sopenharmony_ci		return dev_err_probe(&dev->udev->dev, irq,
128062306a36Sopenharmony_ci				     "No IRQ for privacy GPIO\n");
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	unit = uvc_alloc_entity(UVC_EXT_GPIO_UNIT, UVC_EXT_GPIO_UNIT_ID, 0, 1);
128362306a36Sopenharmony_ci	if (!unit)
128462306a36Sopenharmony_ci		return -ENOMEM;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	unit->gpio.gpio_privacy = gpio_privacy;
128762306a36Sopenharmony_ci	unit->gpio.irq = irq;
128862306a36Sopenharmony_ci	unit->gpio.bControlSize = 1;
128962306a36Sopenharmony_ci	unit->gpio.bmControls = (u8 *)unit + sizeof(*unit);
129062306a36Sopenharmony_ci	unit->gpio.bmControls[0] = 1;
129162306a36Sopenharmony_ci	unit->get_cur = uvc_gpio_get_cur;
129262306a36Sopenharmony_ci	unit->get_info = uvc_gpio_get_info;
129362306a36Sopenharmony_ci	strscpy(unit->name, "GPIO", sizeof(unit->name));
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	list_add_tail(&unit->list, &dev->entities);
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	dev->gpio_unit = unit;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	return 0;
130062306a36Sopenharmony_ci}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_cistatic int uvc_gpio_init_irq(struct uvc_device *dev)
130362306a36Sopenharmony_ci{
130462306a36Sopenharmony_ci	struct uvc_entity *unit = dev->gpio_unit;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	if (!unit || unit->gpio.irq < 0)
130762306a36Sopenharmony_ci		return 0;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	return devm_request_threaded_irq(&dev->udev->dev, unit->gpio.irq, NULL,
131062306a36Sopenharmony_ci					 uvc_gpio_irq,
131162306a36Sopenharmony_ci					 IRQF_ONESHOT | IRQF_TRIGGER_FALLING |
131262306a36Sopenharmony_ci					 IRQF_TRIGGER_RISING,
131362306a36Sopenharmony_ci					 "uvc_privacy_gpio", dev);
131462306a36Sopenharmony_ci}
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci/* ------------------------------------------------------------------------
131762306a36Sopenharmony_ci * UVC device scan
131862306a36Sopenharmony_ci */
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci/*
132162306a36Sopenharmony_ci * Scan the UVC descriptors to locate a chain starting at an Output Terminal
132262306a36Sopenharmony_ci * and containing the following units:
132362306a36Sopenharmony_ci *
132462306a36Sopenharmony_ci * - one or more Output Terminals (USB Streaming or Display)
132562306a36Sopenharmony_ci * - zero or one Processing Unit
132662306a36Sopenharmony_ci * - zero, one or more single-input Selector Units
132762306a36Sopenharmony_ci * - zero or one multiple-input Selector Units, provided all inputs are
132862306a36Sopenharmony_ci *   connected to input terminals
132962306a36Sopenharmony_ci * - zero, one or mode single-input Extension Units
133062306a36Sopenharmony_ci * - one or more Input Terminals (Camera, External or USB Streaming)
133162306a36Sopenharmony_ci *
133262306a36Sopenharmony_ci * The terminal and units must match on of the following structures:
133362306a36Sopenharmony_ci *
133462306a36Sopenharmony_ci * ITT_*(0) -> +---------+    +---------+    +---------+ -> TT_STREAMING(0)
133562306a36Sopenharmony_ci * ...         | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} |    ...
133662306a36Sopenharmony_ci * ITT_*(n) -> +---------+    +---------+    +---------+ -> TT_STREAMING(n)
133762306a36Sopenharmony_ci *
133862306a36Sopenharmony_ci *                 +---------+    +---------+ -> OTT_*(0)
133962306a36Sopenharmony_ci * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} |    ...
134062306a36Sopenharmony_ci *                 +---------+    +---------+ -> OTT_*(n)
134162306a36Sopenharmony_ci *
134262306a36Sopenharmony_ci * The Processing Unit and Extension Units can be in any order. Additional
134362306a36Sopenharmony_ci * Extension Units connected to the main chain as single-unit branches are
134462306a36Sopenharmony_ci * also supported. Single-input Selector Units are ignored.
134562306a36Sopenharmony_ci */
134662306a36Sopenharmony_cistatic int uvc_scan_chain_entity(struct uvc_video_chain *chain,
134762306a36Sopenharmony_ci	struct uvc_entity *entity)
134862306a36Sopenharmony_ci{
134962306a36Sopenharmony_ci	switch (UVC_ENTITY_TYPE(entity)) {
135062306a36Sopenharmony_ci	case UVC_VC_EXTENSION_UNIT:
135162306a36Sopenharmony_ci		uvc_dbg_cont(PROBE, " <- XU %d", entity->id);
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci		if (entity->bNrInPins != 1) {
135462306a36Sopenharmony_ci			uvc_dbg(chain->dev, DESCR,
135562306a36Sopenharmony_ci				"Extension unit %d has more than 1 input pin\n",
135662306a36Sopenharmony_ci				entity->id);
135762306a36Sopenharmony_ci			return -1;
135862306a36Sopenharmony_ci		}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci		break;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	case UVC_VC_PROCESSING_UNIT:
136362306a36Sopenharmony_ci		uvc_dbg_cont(PROBE, " <- PU %d", entity->id);
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci		if (chain->processing != NULL) {
136662306a36Sopenharmony_ci			uvc_dbg(chain->dev, DESCR,
136762306a36Sopenharmony_ci				"Found multiple Processing Units in chain\n");
136862306a36Sopenharmony_ci			return -1;
136962306a36Sopenharmony_ci		}
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci		chain->processing = entity;
137262306a36Sopenharmony_ci		break;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	case UVC_VC_SELECTOR_UNIT:
137562306a36Sopenharmony_ci		uvc_dbg_cont(PROBE, " <- SU %d", entity->id);
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci		/* Single-input selector units are ignored. */
137862306a36Sopenharmony_ci		if (entity->bNrInPins == 1)
137962306a36Sopenharmony_ci			break;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci		if (chain->selector != NULL) {
138262306a36Sopenharmony_ci			uvc_dbg(chain->dev, DESCR,
138362306a36Sopenharmony_ci				"Found multiple Selector Units in chain\n");
138462306a36Sopenharmony_ci			return -1;
138562306a36Sopenharmony_ci		}
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci		chain->selector = entity;
138862306a36Sopenharmony_ci		break;
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	case UVC_ITT_VENDOR_SPECIFIC:
139162306a36Sopenharmony_ci	case UVC_ITT_CAMERA:
139262306a36Sopenharmony_ci	case UVC_ITT_MEDIA_TRANSPORT_INPUT:
139362306a36Sopenharmony_ci		uvc_dbg_cont(PROBE, " <- IT %d\n", entity->id);
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci		break;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	case UVC_OTT_VENDOR_SPECIFIC:
139862306a36Sopenharmony_ci	case UVC_OTT_DISPLAY:
139962306a36Sopenharmony_ci	case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
140062306a36Sopenharmony_ci		uvc_dbg_cont(PROBE, " OT %d", entity->id);
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci		break;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	case UVC_TT_STREAMING:
140562306a36Sopenharmony_ci		if (UVC_ENTITY_IS_ITERM(entity))
140662306a36Sopenharmony_ci			uvc_dbg_cont(PROBE, " <- IT %d\n", entity->id);
140762306a36Sopenharmony_ci		else
140862306a36Sopenharmony_ci			uvc_dbg_cont(PROBE, " OT %d", entity->id);
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci		break;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	default:
141362306a36Sopenharmony_ci		uvc_dbg(chain->dev, DESCR,
141462306a36Sopenharmony_ci			"Unsupported entity type 0x%04x found in chain\n",
141562306a36Sopenharmony_ci			UVC_ENTITY_TYPE(entity));
141662306a36Sopenharmony_ci		return -1;
141762306a36Sopenharmony_ci	}
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	list_add_tail(&entity->chain, &chain->entities);
142062306a36Sopenharmony_ci	return 0;
142162306a36Sopenharmony_ci}
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_cistatic int uvc_scan_chain_forward(struct uvc_video_chain *chain,
142462306a36Sopenharmony_ci	struct uvc_entity *entity, struct uvc_entity *prev)
142562306a36Sopenharmony_ci{
142662306a36Sopenharmony_ci	struct uvc_entity *forward;
142762306a36Sopenharmony_ci	int found;
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	/* Forward scan */
143062306a36Sopenharmony_ci	forward = NULL;
143162306a36Sopenharmony_ci	found = 0;
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	while (1) {
143462306a36Sopenharmony_ci		forward = uvc_entity_by_reference(chain->dev, entity->id,
143562306a36Sopenharmony_ci			forward);
143662306a36Sopenharmony_ci		if (forward == NULL)
143762306a36Sopenharmony_ci			break;
143862306a36Sopenharmony_ci		if (forward == prev)
143962306a36Sopenharmony_ci			continue;
144062306a36Sopenharmony_ci		if (forward->chain.next || forward->chain.prev) {
144162306a36Sopenharmony_ci			uvc_dbg(chain->dev, DESCR,
144262306a36Sopenharmony_ci				"Found reference to entity %d already in chain\n",
144362306a36Sopenharmony_ci				forward->id);
144462306a36Sopenharmony_ci			return -EINVAL;
144562306a36Sopenharmony_ci		}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci		switch (UVC_ENTITY_TYPE(forward)) {
144862306a36Sopenharmony_ci		case UVC_VC_EXTENSION_UNIT:
144962306a36Sopenharmony_ci			if (forward->bNrInPins != 1) {
145062306a36Sopenharmony_ci				uvc_dbg(chain->dev, DESCR,
145162306a36Sopenharmony_ci					"Extension unit %d has more than 1 input pin\n",
145262306a36Sopenharmony_ci					forward->id);
145362306a36Sopenharmony_ci				return -EINVAL;
145462306a36Sopenharmony_ci			}
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci			/*
145762306a36Sopenharmony_ci			 * Some devices reference an output terminal as the
145862306a36Sopenharmony_ci			 * source of extension units. This is incorrect, as
145962306a36Sopenharmony_ci			 * output terminals only have an input pin, and thus
146062306a36Sopenharmony_ci			 * can't be connected to any entity in the forward
146162306a36Sopenharmony_ci			 * direction. The resulting topology would cause issues
146262306a36Sopenharmony_ci			 * when registering the media controller graph. To
146362306a36Sopenharmony_ci			 * avoid this problem, connect the extension unit to
146462306a36Sopenharmony_ci			 * the source of the output terminal instead.
146562306a36Sopenharmony_ci			 */
146662306a36Sopenharmony_ci			if (UVC_ENTITY_IS_OTERM(entity)) {
146762306a36Sopenharmony_ci				struct uvc_entity *source;
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci				source = uvc_entity_by_id(chain->dev,
147062306a36Sopenharmony_ci							  entity->baSourceID[0]);
147162306a36Sopenharmony_ci				if (!source) {
147262306a36Sopenharmony_ci					uvc_dbg(chain->dev, DESCR,
147362306a36Sopenharmony_ci						"Can't connect extension unit %u in chain\n",
147462306a36Sopenharmony_ci						forward->id);
147562306a36Sopenharmony_ci					break;
147662306a36Sopenharmony_ci				}
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci				forward->baSourceID[0] = source->id;
147962306a36Sopenharmony_ci			}
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci			list_add_tail(&forward->chain, &chain->entities);
148262306a36Sopenharmony_ci			if (!found)
148362306a36Sopenharmony_ci				uvc_dbg_cont(PROBE, " (->");
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci			uvc_dbg_cont(PROBE, " XU %d", forward->id);
148662306a36Sopenharmony_ci			found = 1;
148762306a36Sopenharmony_ci			break;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci		case UVC_OTT_VENDOR_SPECIFIC:
149062306a36Sopenharmony_ci		case UVC_OTT_DISPLAY:
149162306a36Sopenharmony_ci		case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
149262306a36Sopenharmony_ci		case UVC_TT_STREAMING:
149362306a36Sopenharmony_ci			if (UVC_ENTITY_IS_ITERM(forward)) {
149462306a36Sopenharmony_ci				uvc_dbg(chain->dev, DESCR,
149562306a36Sopenharmony_ci					"Unsupported input terminal %u\n",
149662306a36Sopenharmony_ci					forward->id);
149762306a36Sopenharmony_ci				return -EINVAL;
149862306a36Sopenharmony_ci			}
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci			if (UVC_ENTITY_IS_OTERM(entity)) {
150162306a36Sopenharmony_ci				uvc_dbg(chain->dev, DESCR,
150262306a36Sopenharmony_ci					"Unsupported connection between output terminals %u and %u\n",
150362306a36Sopenharmony_ci					entity->id, forward->id);
150462306a36Sopenharmony_ci				break;
150562306a36Sopenharmony_ci			}
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci			list_add_tail(&forward->chain, &chain->entities);
150862306a36Sopenharmony_ci			if (!found)
150962306a36Sopenharmony_ci				uvc_dbg_cont(PROBE, " (->");
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci			uvc_dbg_cont(PROBE, " OT %d", forward->id);
151262306a36Sopenharmony_ci			found = 1;
151362306a36Sopenharmony_ci			break;
151462306a36Sopenharmony_ci		}
151562306a36Sopenharmony_ci	}
151662306a36Sopenharmony_ci	if (found)
151762306a36Sopenharmony_ci		uvc_dbg_cont(PROBE, ")");
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	return 0;
152062306a36Sopenharmony_ci}
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_cistatic int uvc_scan_chain_backward(struct uvc_video_chain *chain,
152362306a36Sopenharmony_ci	struct uvc_entity **_entity)
152462306a36Sopenharmony_ci{
152562306a36Sopenharmony_ci	struct uvc_entity *entity = *_entity;
152662306a36Sopenharmony_ci	struct uvc_entity *term;
152762306a36Sopenharmony_ci	int id = -EINVAL, i;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	switch (UVC_ENTITY_TYPE(entity)) {
153062306a36Sopenharmony_ci	case UVC_VC_EXTENSION_UNIT:
153162306a36Sopenharmony_ci	case UVC_VC_PROCESSING_UNIT:
153262306a36Sopenharmony_ci		id = entity->baSourceID[0];
153362306a36Sopenharmony_ci		break;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	case UVC_VC_SELECTOR_UNIT:
153662306a36Sopenharmony_ci		/* Single-input selector units are ignored. */
153762306a36Sopenharmony_ci		if (entity->bNrInPins == 1) {
153862306a36Sopenharmony_ci			id = entity->baSourceID[0];
153962306a36Sopenharmony_ci			break;
154062306a36Sopenharmony_ci		}
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci		uvc_dbg_cont(PROBE, " <- IT");
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci		chain->selector = entity;
154562306a36Sopenharmony_ci		for (i = 0; i < entity->bNrInPins; ++i) {
154662306a36Sopenharmony_ci			id = entity->baSourceID[i];
154762306a36Sopenharmony_ci			term = uvc_entity_by_id(chain->dev, id);
154862306a36Sopenharmony_ci			if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
154962306a36Sopenharmony_ci				uvc_dbg(chain->dev, DESCR,
155062306a36Sopenharmony_ci					"Selector unit %d input %d isn't connected to an input terminal\n",
155162306a36Sopenharmony_ci					entity->id, i);
155262306a36Sopenharmony_ci				return -1;
155362306a36Sopenharmony_ci			}
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci			if (term->chain.next || term->chain.prev) {
155662306a36Sopenharmony_ci				uvc_dbg(chain->dev, DESCR,
155762306a36Sopenharmony_ci					"Found reference to entity %d already in chain\n",
155862306a36Sopenharmony_ci					term->id);
155962306a36Sopenharmony_ci				return -EINVAL;
156062306a36Sopenharmony_ci			}
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci			uvc_dbg_cont(PROBE, " %d", term->id);
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci			list_add_tail(&term->chain, &chain->entities);
156562306a36Sopenharmony_ci			uvc_scan_chain_forward(chain, term, entity);
156662306a36Sopenharmony_ci		}
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci		uvc_dbg_cont(PROBE, "\n");
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci		id = 0;
157162306a36Sopenharmony_ci		break;
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	case UVC_ITT_VENDOR_SPECIFIC:
157462306a36Sopenharmony_ci	case UVC_ITT_CAMERA:
157562306a36Sopenharmony_ci	case UVC_ITT_MEDIA_TRANSPORT_INPUT:
157662306a36Sopenharmony_ci	case UVC_OTT_VENDOR_SPECIFIC:
157762306a36Sopenharmony_ci	case UVC_OTT_DISPLAY:
157862306a36Sopenharmony_ci	case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
157962306a36Sopenharmony_ci	case UVC_TT_STREAMING:
158062306a36Sopenharmony_ci		id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0;
158162306a36Sopenharmony_ci		break;
158262306a36Sopenharmony_ci	}
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	if (id <= 0) {
158562306a36Sopenharmony_ci		*_entity = NULL;
158662306a36Sopenharmony_ci		return id;
158762306a36Sopenharmony_ci	}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	entity = uvc_entity_by_id(chain->dev, id);
159062306a36Sopenharmony_ci	if (entity == NULL) {
159162306a36Sopenharmony_ci		uvc_dbg(chain->dev, DESCR,
159262306a36Sopenharmony_ci			"Found reference to unknown entity %d\n", id);
159362306a36Sopenharmony_ci		return -EINVAL;
159462306a36Sopenharmony_ci	}
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	*_entity = entity;
159762306a36Sopenharmony_ci	return 0;
159862306a36Sopenharmony_ci}
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_cistatic int uvc_scan_chain(struct uvc_video_chain *chain,
160162306a36Sopenharmony_ci			  struct uvc_entity *term)
160262306a36Sopenharmony_ci{
160362306a36Sopenharmony_ci	struct uvc_entity *entity, *prev;
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	uvc_dbg(chain->dev, PROBE, "Scanning UVC chain:");
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	entity = term;
160862306a36Sopenharmony_ci	prev = NULL;
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	while (entity != NULL) {
161162306a36Sopenharmony_ci		/* Entity must not be part of an existing chain */
161262306a36Sopenharmony_ci		if (entity->chain.next || entity->chain.prev) {
161362306a36Sopenharmony_ci			uvc_dbg(chain->dev, DESCR,
161462306a36Sopenharmony_ci				"Found reference to entity %d already in chain\n",
161562306a36Sopenharmony_ci				entity->id);
161662306a36Sopenharmony_ci			return -EINVAL;
161762306a36Sopenharmony_ci		}
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci		/* Process entity */
162062306a36Sopenharmony_ci		if (uvc_scan_chain_entity(chain, entity) < 0)
162162306a36Sopenharmony_ci			return -EINVAL;
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci		/* Forward scan */
162462306a36Sopenharmony_ci		if (uvc_scan_chain_forward(chain, entity, prev) < 0)
162562306a36Sopenharmony_ci			return -EINVAL;
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci		/* Backward scan */
162862306a36Sopenharmony_ci		prev = entity;
162962306a36Sopenharmony_ci		if (uvc_scan_chain_backward(chain, &entity) < 0)
163062306a36Sopenharmony_ci			return -EINVAL;
163162306a36Sopenharmony_ci	}
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	return 0;
163462306a36Sopenharmony_ci}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_cistatic unsigned int uvc_print_terms(struct list_head *terms, u16 dir,
163762306a36Sopenharmony_ci		char *buffer)
163862306a36Sopenharmony_ci{
163962306a36Sopenharmony_ci	struct uvc_entity *term;
164062306a36Sopenharmony_ci	unsigned int nterms = 0;
164162306a36Sopenharmony_ci	char *p = buffer;
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	list_for_each_entry(term, terms, chain) {
164462306a36Sopenharmony_ci		if (!UVC_ENTITY_IS_TERM(term) ||
164562306a36Sopenharmony_ci		    UVC_TERM_DIRECTION(term) != dir)
164662306a36Sopenharmony_ci			continue;
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci		if (nterms)
164962306a36Sopenharmony_ci			p += sprintf(p, ",");
165062306a36Sopenharmony_ci		if (++nterms >= 4) {
165162306a36Sopenharmony_ci			p += sprintf(p, "...");
165262306a36Sopenharmony_ci			break;
165362306a36Sopenharmony_ci		}
165462306a36Sopenharmony_ci		p += sprintf(p, "%u", term->id);
165562306a36Sopenharmony_ci	}
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	return p - buffer;
165862306a36Sopenharmony_ci}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_cistatic const char *uvc_print_chain(struct uvc_video_chain *chain)
166162306a36Sopenharmony_ci{
166262306a36Sopenharmony_ci	static char buffer[43];
166362306a36Sopenharmony_ci	char *p = buffer;
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p);
166662306a36Sopenharmony_ci	p += sprintf(p, " -> ");
166762306a36Sopenharmony_ci	uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p);
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	return buffer;
167062306a36Sopenharmony_ci}
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_cistatic struct uvc_video_chain *uvc_alloc_chain(struct uvc_device *dev)
167362306a36Sopenharmony_ci{
167462306a36Sopenharmony_ci	struct uvc_video_chain *chain;
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	chain = kzalloc(sizeof(*chain), GFP_KERNEL);
167762306a36Sopenharmony_ci	if (chain == NULL)
167862306a36Sopenharmony_ci		return NULL;
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci	INIT_LIST_HEAD(&chain->entities);
168162306a36Sopenharmony_ci	mutex_init(&chain->ctrl_mutex);
168262306a36Sopenharmony_ci	chain->dev = dev;
168362306a36Sopenharmony_ci	v4l2_prio_init(&chain->prio);
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	return chain;
168662306a36Sopenharmony_ci}
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci/*
168962306a36Sopenharmony_ci * Fallback heuristic for devices that don't connect units and terminals in a
169062306a36Sopenharmony_ci * valid chain.
169162306a36Sopenharmony_ci *
169262306a36Sopenharmony_ci * Some devices have invalid baSourceID references, causing uvc_scan_chain()
169362306a36Sopenharmony_ci * to fail, but if we just take the entities we can find and put them together
169462306a36Sopenharmony_ci * in the most sensible chain we can think of, turns out they do work anyway.
169562306a36Sopenharmony_ci * Note: This heuristic assumes there is a single chain.
169662306a36Sopenharmony_ci *
169762306a36Sopenharmony_ci * At the time of writing, devices known to have such a broken chain are
169862306a36Sopenharmony_ci *  - Acer Integrated Camera (5986:055a)
169962306a36Sopenharmony_ci *  - Realtek rtl157a7 (0bda:57a7)
170062306a36Sopenharmony_ci */
170162306a36Sopenharmony_cistatic int uvc_scan_fallback(struct uvc_device *dev)
170262306a36Sopenharmony_ci{
170362306a36Sopenharmony_ci	struct uvc_video_chain *chain;
170462306a36Sopenharmony_ci	struct uvc_entity *iterm = NULL;
170562306a36Sopenharmony_ci	struct uvc_entity *oterm = NULL;
170662306a36Sopenharmony_ci	struct uvc_entity *entity;
170762306a36Sopenharmony_ci	struct uvc_entity *prev;
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	/*
171062306a36Sopenharmony_ci	 * Start by locating the input and output terminals. We only support
171162306a36Sopenharmony_ci	 * devices with exactly one of each for now.
171262306a36Sopenharmony_ci	 */
171362306a36Sopenharmony_ci	list_for_each_entry(entity, &dev->entities, list) {
171462306a36Sopenharmony_ci		if (UVC_ENTITY_IS_ITERM(entity)) {
171562306a36Sopenharmony_ci			if (iterm)
171662306a36Sopenharmony_ci				return -EINVAL;
171762306a36Sopenharmony_ci			iterm = entity;
171862306a36Sopenharmony_ci		}
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci		if (UVC_ENTITY_IS_OTERM(entity)) {
172162306a36Sopenharmony_ci			if (oterm)
172262306a36Sopenharmony_ci				return -EINVAL;
172362306a36Sopenharmony_ci			oterm = entity;
172462306a36Sopenharmony_ci		}
172562306a36Sopenharmony_ci	}
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	if (iterm == NULL || oterm == NULL)
172862306a36Sopenharmony_ci		return -EINVAL;
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci	/* Allocate the chain and fill it. */
173162306a36Sopenharmony_ci	chain = uvc_alloc_chain(dev);
173262306a36Sopenharmony_ci	if (chain == NULL)
173362306a36Sopenharmony_ci		return -ENOMEM;
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	if (uvc_scan_chain_entity(chain, oterm) < 0)
173662306a36Sopenharmony_ci		goto error;
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	prev = oterm;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	/*
174162306a36Sopenharmony_ci	 * Add all Processing and Extension Units with two pads. The order
174262306a36Sopenharmony_ci	 * doesn't matter much, use reverse list traversal to connect units in
174362306a36Sopenharmony_ci	 * UVC descriptor order as we build the chain from output to input. This
174462306a36Sopenharmony_ci	 * leads to units appearing in the order meant by the manufacturer for
174562306a36Sopenharmony_ci	 * the cameras known to require this heuristic.
174662306a36Sopenharmony_ci	 */
174762306a36Sopenharmony_ci	list_for_each_entry_reverse(entity, &dev->entities, list) {
174862306a36Sopenharmony_ci		if (entity->type != UVC_VC_PROCESSING_UNIT &&
174962306a36Sopenharmony_ci		    entity->type != UVC_VC_EXTENSION_UNIT)
175062306a36Sopenharmony_ci			continue;
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci		if (entity->num_pads != 2)
175362306a36Sopenharmony_ci			continue;
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci		if (uvc_scan_chain_entity(chain, entity) < 0)
175662306a36Sopenharmony_ci			goto error;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci		prev->baSourceID[0] = entity->id;
175962306a36Sopenharmony_ci		prev = entity;
176062306a36Sopenharmony_ci	}
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	if (uvc_scan_chain_entity(chain, iterm) < 0)
176362306a36Sopenharmony_ci		goto error;
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	prev->baSourceID[0] = iterm->id;
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	list_add_tail(&chain->list, &dev->chains);
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	uvc_dbg(dev, PROBE, "Found a video chain by fallback heuristic (%s)\n",
177062306a36Sopenharmony_ci		uvc_print_chain(chain));
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	return 0;
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_cierror:
177562306a36Sopenharmony_ci	kfree(chain);
177662306a36Sopenharmony_ci	return -EINVAL;
177762306a36Sopenharmony_ci}
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci/*
178062306a36Sopenharmony_ci * Scan the device for video chains and register video devices.
178162306a36Sopenharmony_ci *
178262306a36Sopenharmony_ci * Chains are scanned starting at their output terminals and walked backwards.
178362306a36Sopenharmony_ci */
178462306a36Sopenharmony_cistatic int uvc_scan_device(struct uvc_device *dev)
178562306a36Sopenharmony_ci{
178662306a36Sopenharmony_ci	struct uvc_video_chain *chain;
178762306a36Sopenharmony_ci	struct uvc_entity *term;
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	list_for_each_entry(term, &dev->entities, list) {
179062306a36Sopenharmony_ci		if (!UVC_ENTITY_IS_OTERM(term))
179162306a36Sopenharmony_ci			continue;
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci		/*
179462306a36Sopenharmony_ci		 * If the terminal is already included in a chain, skip it.
179562306a36Sopenharmony_ci		 * This can happen for chains that have multiple output
179662306a36Sopenharmony_ci		 * terminals, where all output terminals beside the first one
179762306a36Sopenharmony_ci		 * will be inserted in the chain in forward scans.
179862306a36Sopenharmony_ci		 */
179962306a36Sopenharmony_ci		if (term->chain.next || term->chain.prev)
180062306a36Sopenharmony_ci			continue;
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci		chain = uvc_alloc_chain(dev);
180362306a36Sopenharmony_ci		if (chain == NULL)
180462306a36Sopenharmony_ci			return -ENOMEM;
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci		term->flags |= UVC_ENTITY_FLAG_DEFAULT;
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci		if (uvc_scan_chain(chain, term) < 0) {
180962306a36Sopenharmony_ci			kfree(chain);
181062306a36Sopenharmony_ci			continue;
181162306a36Sopenharmony_ci		}
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci		uvc_dbg(dev, PROBE, "Found a valid video chain (%s)\n",
181462306a36Sopenharmony_ci			uvc_print_chain(chain));
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci		list_add_tail(&chain->list, &dev->chains);
181762306a36Sopenharmony_ci	}
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	if (list_empty(&dev->chains))
182062306a36Sopenharmony_ci		uvc_scan_fallback(dev);
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	if (list_empty(&dev->chains)) {
182362306a36Sopenharmony_ci		dev_info(&dev->udev->dev, "No valid video chain found.\n");
182462306a36Sopenharmony_ci		return -1;
182562306a36Sopenharmony_ci	}
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	/* Add GPIO entity to the first chain. */
182862306a36Sopenharmony_ci	if (dev->gpio_unit) {
182962306a36Sopenharmony_ci		chain = list_first_entry(&dev->chains,
183062306a36Sopenharmony_ci					 struct uvc_video_chain, list);
183162306a36Sopenharmony_ci		list_add_tail(&dev->gpio_unit->chain, &chain->entities);
183262306a36Sopenharmony_ci	}
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	return 0;
183562306a36Sopenharmony_ci}
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci/* ------------------------------------------------------------------------
183862306a36Sopenharmony_ci * Video device registration and unregistration
183962306a36Sopenharmony_ci */
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci/*
184262306a36Sopenharmony_ci * Delete the UVC device.
184362306a36Sopenharmony_ci *
184462306a36Sopenharmony_ci * Called by the kernel when the last reference to the uvc_device structure
184562306a36Sopenharmony_ci * is released.
184662306a36Sopenharmony_ci *
184762306a36Sopenharmony_ci * As this function is called after or during disconnect(), all URBs have
184862306a36Sopenharmony_ci * already been cancelled by the USB core. There is no need to kill the
184962306a36Sopenharmony_ci * interrupt URB manually.
185062306a36Sopenharmony_ci */
185162306a36Sopenharmony_cistatic void uvc_delete(struct kref *kref)
185262306a36Sopenharmony_ci{
185362306a36Sopenharmony_ci	struct uvc_device *dev = container_of(kref, struct uvc_device, ref);
185462306a36Sopenharmony_ci	struct list_head *p, *n;
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	uvc_status_cleanup(dev);
185762306a36Sopenharmony_ci	uvc_ctrl_cleanup_device(dev);
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci	usb_put_intf(dev->intf);
186062306a36Sopenharmony_ci	usb_put_dev(dev->udev);
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
186362306a36Sopenharmony_ci	media_device_cleanup(&dev->mdev);
186462306a36Sopenharmony_ci#endif
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	list_for_each_safe(p, n, &dev->chains) {
186762306a36Sopenharmony_ci		struct uvc_video_chain *chain;
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci		chain = list_entry(p, struct uvc_video_chain, list);
187062306a36Sopenharmony_ci		kfree(chain);
187162306a36Sopenharmony_ci	}
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	list_for_each_safe(p, n, &dev->entities) {
187462306a36Sopenharmony_ci		struct uvc_entity *entity;
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci		entity = list_entry(p, struct uvc_entity, list);
187762306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
187862306a36Sopenharmony_ci		uvc_mc_cleanup_entity(entity);
187962306a36Sopenharmony_ci#endif
188062306a36Sopenharmony_ci		kfree(entity);
188162306a36Sopenharmony_ci	}
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	list_for_each_safe(p, n, &dev->streams) {
188462306a36Sopenharmony_ci		struct uvc_streaming *streaming;
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci		streaming = list_entry(p, struct uvc_streaming, list);
188762306a36Sopenharmony_ci		usb_driver_release_interface(&uvc_driver.driver,
188862306a36Sopenharmony_ci			streaming->intf);
188962306a36Sopenharmony_ci		uvc_stream_delete(streaming);
189062306a36Sopenharmony_ci	}
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	kfree(dev);
189362306a36Sopenharmony_ci}
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_cistatic void uvc_release(struct video_device *vdev)
189662306a36Sopenharmony_ci{
189762306a36Sopenharmony_ci	struct uvc_streaming *stream = video_get_drvdata(vdev);
189862306a36Sopenharmony_ci	struct uvc_device *dev = stream->dev;
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci	kref_put(&dev->ref, uvc_delete);
190162306a36Sopenharmony_ci}
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci/*
190462306a36Sopenharmony_ci * Unregister the video devices.
190562306a36Sopenharmony_ci */
190662306a36Sopenharmony_cistatic void uvc_unregister_video(struct uvc_device *dev)
190762306a36Sopenharmony_ci{
190862306a36Sopenharmony_ci	struct uvc_streaming *stream;
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	list_for_each_entry(stream, &dev->streams, list) {
191162306a36Sopenharmony_ci		if (!video_is_registered(&stream->vdev))
191262306a36Sopenharmony_ci			continue;
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci		video_unregister_device(&stream->vdev);
191562306a36Sopenharmony_ci		video_unregister_device(&stream->meta.vdev);
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci		uvc_debugfs_cleanup_stream(stream);
191862306a36Sopenharmony_ci	}
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	uvc_status_unregister(dev);
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci	if (dev->vdev.dev)
192362306a36Sopenharmony_ci		v4l2_device_unregister(&dev->vdev);
192462306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
192562306a36Sopenharmony_ci	if (media_devnode_is_registered(dev->mdev.devnode))
192662306a36Sopenharmony_ci		media_device_unregister(&dev->mdev);
192762306a36Sopenharmony_ci#endif
192862306a36Sopenharmony_ci}
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ciint uvc_register_video_device(struct uvc_device *dev,
193162306a36Sopenharmony_ci			      struct uvc_streaming *stream,
193262306a36Sopenharmony_ci			      struct video_device *vdev,
193362306a36Sopenharmony_ci			      struct uvc_video_queue *queue,
193462306a36Sopenharmony_ci			      enum v4l2_buf_type type,
193562306a36Sopenharmony_ci			      const struct v4l2_file_operations *fops,
193662306a36Sopenharmony_ci			      const struct v4l2_ioctl_ops *ioctl_ops)
193762306a36Sopenharmony_ci{
193862306a36Sopenharmony_ci	int ret;
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	/* Initialize the video buffers queue. */
194162306a36Sopenharmony_ci	ret = uvc_queue_init(queue, type, !uvc_no_drop_param);
194262306a36Sopenharmony_ci	if (ret)
194362306a36Sopenharmony_ci		return ret;
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	/* Register the device with V4L. */
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	/*
194862306a36Sopenharmony_ci	 * We already hold a reference to dev->udev. The video device will be
194962306a36Sopenharmony_ci	 * unregistered before the reference is released, so we don't need to
195062306a36Sopenharmony_ci	 * get another one.
195162306a36Sopenharmony_ci	 */
195262306a36Sopenharmony_ci	vdev->v4l2_dev = &dev->vdev;
195362306a36Sopenharmony_ci	vdev->fops = fops;
195462306a36Sopenharmony_ci	vdev->ioctl_ops = ioctl_ops;
195562306a36Sopenharmony_ci	vdev->release = uvc_release;
195662306a36Sopenharmony_ci	vdev->prio = &stream->chain->prio;
195762306a36Sopenharmony_ci	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
195862306a36Sopenharmony_ci		vdev->vfl_dir = VFL_DIR_TX;
195962306a36Sopenharmony_ci	else
196062306a36Sopenharmony_ci		vdev->vfl_dir = VFL_DIR_RX;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	switch (type) {
196362306a36Sopenharmony_ci	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
196462306a36Sopenharmony_ci	default:
196562306a36Sopenharmony_ci		vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
196662306a36Sopenharmony_ci		break;
196762306a36Sopenharmony_ci	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
196862306a36Sopenharmony_ci		vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
196962306a36Sopenharmony_ci		break;
197062306a36Sopenharmony_ci	case V4L2_BUF_TYPE_META_CAPTURE:
197162306a36Sopenharmony_ci		vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
197262306a36Sopenharmony_ci		break;
197362306a36Sopenharmony_ci	}
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci	strscpy(vdev->name, dev->name, sizeof(vdev->name));
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci	/*
197862306a36Sopenharmony_ci	 * Set the driver data before calling video_register_device, otherwise
197962306a36Sopenharmony_ci	 * the file open() handler might race us.
198062306a36Sopenharmony_ci	 */
198162306a36Sopenharmony_ci	video_set_drvdata(vdev, stream);
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
198462306a36Sopenharmony_ci	if (ret < 0) {
198562306a36Sopenharmony_ci		dev_err(&stream->intf->dev,
198662306a36Sopenharmony_ci			"Failed to register %s device (%d).\n",
198762306a36Sopenharmony_ci			v4l2_type_names[type], ret);
198862306a36Sopenharmony_ci		return ret;
198962306a36Sopenharmony_ci	}
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci	kref_get(&dev->ref);
199262306a36Sopenharmony_ci	return 0;
199362306a36Sopenharmony_ci}
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_cistatic int uvc_register_video(struct uvc_device *dev,
199662306a36Sopenharmony_ci		struct uvc_streaming *stream)
199762306a36Sopenharmony_ci{
199862306a36Sopenharmony_ci	int ret;
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci	/* Initialize the streaming interface with default parameters. */
200162306a36Sopenharmony_ci	ret = uvc_video_init(stream);
200262306a36Sopenharmony_ci	if (ret < 0) {
200362306a36Sopenharmony_ci		dev_err(&stream->intf->dev,
200462306a36Sopenharmony_ci			"Failed to initialize the device (%d).\n", ret);
200562306a36Sopenharmony_ci		return ret;
200662306a36Sopenharmony_ci	}
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
200962306a36Sopenharmony_ci		stream->chain->caps |= V4L2_CAP_VIDEO_CAPTURE
201062306a36Sopenharmony_ci			| V4L2_CAP_META_CAPTURE;
201162306a36Sopenharmony_ci	else
201262306a36Sopenharmony_ci		stream->chain->caps |= V4L2_CAP_VIDEO_OUTPUT;
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	uvc_debugfs_init_stream(stream);
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci	/* Register the device with V4L. */
201762306a36Sopenharmony_ci	return uvc_register_video_device(dev, stream, &stream->vdev,
201862306a36Sopenharmony_ci					 &stream->queue, stream->type,
201962306a36Sopenharmony_ci					 &uvc_fops, &uvc_ioctl_ops);
202062306a36Sopenharmony_ci}
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci/*
202362306a36Sopenharmony_ci * Register all video devices in all chains.
202462306a36Sopenharmony_ci */
202562306a36Sopenharmony_cistatic int uvc_register_terms(struct uvc_device *dev,
202662306a36Sopenharmony_ci	struct uvc_video_chain *chain)
202762306a36Sopenharmony_ci{
202862306a36Sopenharmony_ci	struct uvc_streaming *stream;
202962306a36Sopenharmony_ci	struct uvc_entity *term;
203062306a36Sopenharmony_ci	int ret;
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	list_for_each_entry(term, &chain->entities, chain) {
203362306a36Sopenharmony_ci		if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
203462306a36Sopenharmony_ci			continue;
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci		stream = uvc_stream_by_id(dev, term->id);
203762306a36Sopenharmony_ci		if (stream == NULL) {
203862306a36Sopenharmony_ci			dev_info(&dev->udev->dev,
203962306a36Sopenharmony_ci				 "No streaming interface found for terminal %u.",
204062306a36Sopenharmony_ci				 term->id);
204162306a36Sopenharmony_ci			continue;
204262306a36Sopenharmony_ci		}
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci		stream->chain = chain;
204562306a36Sopenharmony_ci		ret = uvc_register_video(dev, stream);
204662306a36Sopenharmony_ci		if (ret < 0)
204762306a36Sopenharmony_ci			return ret;
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_ci		/*
205062306a36Sopenharmony_ci		 * Register a metadata node, but ignore a possible failure,
205162306a36Sopenharmony_ci		 * complete registration of video nodes anyway.
205262306a36Sopenharmony_ci		 */
205362306a36Sopenharmony_ci		uvc_meta_register(stream);
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci		term->vdev = &stream->vdev;
205662306a36Sopenharmony_ci	}
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci	return 0;
205962306a36Sopenharmony_ci}
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_cistatic int uvc_register_chains(struct uvc_device *dev)
206262306a36Sopenharmony_ci{
206362306a36Sopenharmony_ci	struct uvc_video_chain *chain;
206462306a36Sopenharmony_ci	int ret;
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	list_for_each_entry(chain, &dev->chains, list) {
206762306a36Sopenharmony_ci		ret = uvc_register_terms(dev, chain);
206862306a36Sopenharmony_ci		if (ret < 0)
206962306a36Sopenharmony_ci			return ret;
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
207262306a36Sopenharmony_ci		ret = uvc_mc_register_entities(chain);
207362306a36Sopenharmony_ci		if (ret < 0)
207462306a36Sopenharmony_ci			dev_info(&dev->udev->dev,
207562306a36Sopenharmony_ci				 "Failed to register entities (%d).\n", ret);
207662306a36Sopenharmony_ci#endif
207762306a36Sopenharmony_ci	}
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci	return 0;
208062306a36Sopenharmony_ci}
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci/* ------------------------------------------------------------------------
208362306a36Sopenharmony_ci * USB probe, disconnect, suspend and resume
208462306a36Sopenharmony_ci */
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_none = { 0 };
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_cistatic int uvc_probe(struct usb_interface *intf,
208962306a36Sopenharmony_ci		     const struct usb_device_id *id)
209062306a36Sopenharmony_ci{
209162306a36Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(intf);
209262306a36Sopenharmony_ci	struct uvc_device *dev;
209362306a36Sopenharmony_ci	const struct uvc_device_info *info =
209462306a36Sopenharmony_ci		(const struct uvc_device_info *)id->driver_info;
209562306a36Sopenharmony_ci	int function;
209662306a36Sopenharmony_ci	int ret;
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	/* Allocate memory for the device and initialize it. */
209962306a36Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
210062306a36Sopenharmony_ci	if (dev == NULL)
210162306a36Sopenharmony_ci		return -ENOMEM;
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	INIT_LIST_HEAD(&dev->entities);
210462306a36Sopenharmony_ci	INIT_LIST_HEAD(&dev->chains);
210562306a36Sopenharmony_ci	INIT_LIST_HEAD(&dev->streams);
210662306a36Sopenharmony_ci	kref_init(&dev->ref);
210762306a36Sopenharmony_ci	atomic_set(&dev->nmappings, 0);
210862306a36Sopenharmony_ci	mutex_init(&dev->lock);
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	dev->udev = usb_get_dev(udev);
211162306a36Sopenharmony_ci	dev->intf = usb_get_intf(intf);
211262306a36Sopenharmony_ci	dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
211362306a36Sopenharmony_ci	dev->info = info ? info : &uvc_quirk_none;
211462306a36Sopenharmony_ci	dev->quirks = uvc_quirks_param == -1
211562306a36Sopenharmony_ci		    ? dev->info->quirks : uvc_quirks_param;
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	if (id->idVendor && id->idProduct)
211862306a36Sopenharmony_ci		uvc_dbg(dev, PROBE, "Probing known UVC device %s (%04x:%04x)\n",
211962306a36Sopenharmony_ci			udev->devpath, id->idVendor, id->idProduct);
212062306a36Sopenharmony_ci	else
212162306a36Sopenharmony_ci		uvc_dbg(dev, PROBE, "Probing generic UVC device %s\n",
212262306a36Sopenharmony_ci			udev->devpath);
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci	if (udev->product != NULL)
212562306a36Sopenharmony_ci		strscpy(dev->name, udev->product, sizeof(dev->name));
212662306a36Sopenharmony_ci	else
212762306a36Sopenharmony_ci		snprintf(dev->name, sizeof(dev->name),
212862306a36Sopenharmony_ci			 "UVC Camera (%04x:%04x)",
212962306a36Sopenharmony_ci			 le16_to_cpu(udev->descriptor.idVendor),
213062306a36Sopenharmony_ci			 le16_to_cpu(udev->descriptor.idProduct));
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	/*
213362306a36Sopenharmony_ci	 * Add iFunction or iInterface to names when available as additional
213462306a36Sopenharmony_ci	 * distinguishers between interfaces. iFunction is prioritized over
213562306a36Sopenharmony_ci	 * iInterface which matches Windows behavior at the point of writing.
213662306a36Sopenharmony_ci	 */
213762306a36Sopenharmony_ci	if (intf->intf_assoc && intf->intf_assoc->iFunction != 0)
213862306a36Sopenharmony_ci		function = intf->intf_assoc->iFunction;
213962306a36Sopenharmony_ci	else
214062306a36Sopenharmony_ci		function = intf->cur_altsetting->desc.iInterface;
214162306a36Sopenharmony_ci	if (function != 0) {
214262306a36Sopenharmony_ci		size_t len;
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_ci		strlcat(dev->name, ": ", sizeof(dev->name));
214562306a36Sopenharmony_ci		len = strlen(dev->name);
214662306a36Sopenharmony_ci		usb_string(udev, function, dev->name + len,
214762306a36Sopenharmony_ci			   sizeof(dev->name) - len);
214862306a36Sopenharmony_ci	}
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	/* Initialize the media device. */
215162306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
215262306a36Sopenharmony_ci	dev->mdev.dev = &intf->dev;
215362306a36Sopenharmony_ci	strscpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
215462306a36Sopenharmony_ci	if (udev->serial)
215562306a36Sopenharmony_ci		strscpy(dev->mdev.serial, udev->serial,
215662306a36Sopenharmony_ci			sizeof(dev->mdev.serial));
215762306a36Sopenharmony_ci	usb_make_path(udev, dev->mdev.bus_info, sizeof(dev->mdev.bus_info));
215862306a36Sopenharmony_ci	dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
215962306a36Sopenharmony_ci	media_device_init(&dev->mdev);
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci	dev->vdev.mdev = &dev->mdev;
216262306a36Sopenharmony_ci#endif
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci	/* Parse the Video Class control descriptor. */
216562306a36Sopenharmony_ci	if (uvc_parse_control(dev) < 0) {
216662306a36Sopenharmony_ci		uvc_dbg(dev, PROBE, "Unable to parse UVC descriptors\n");
216762306a36Sopenharmony_ci		goto error;
216862306a36Sopenharmony_ci	}
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci	/* Parse the associated GPIOs. */
217162306a36Sopenharmony_ci	if (uvc_gpio_parse(dev) < 0) {
217262306a36Sopenharmony_ci		uvc_dbg(dev, PROBE, "Unable to parse UVC GPIOs\n");
217362306a36Sopenharmony_ci		goto error;
217462306a36Sopenharmony_ci	}
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	dev_info(&dev->udev->dev, "Found UVC %u.%02x device %s (%04x:%04x)\n",
217762306a36Sopenharmony_ci		 dev->uvc_version >> 8, dev->uvc_version & 0xff,
217862306a36Sopenharmony_ci		 udev->product ? udev->product : "<unnamed>",
217962306a36Sopenharmony_ci		 le16_to_cpu(udev->descriptor.idVendor),
218062306a36Sopenharmony_ci		 le16_to_cpu(udev->descriptor.idProduct));
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci	if (dev->quirks != dev->info->quirks) {
218362306a36Sopenharmony_ci		dev_info(&dev->udev->dev,
218462306a36Sopenharmony_ci			 "Forcing device quirks to 0x%x by module parameter for testing purpose.\n",
218562306a36Sopenharmony_ci			 dev->quirks);
218662306a36Sopenharmony_ci		dev_info(&dev->udev->dev,
218762306a36Sopenharmony_ci			 "Please report required quirks to the linux-media mailing list.\n");
218862306a36Sopenharmony_ci	}
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	if (dev->info->uvc_version) {
219162306a36Sopenharmony_ci		dev->uvc_version = dev->info->uvc_version;
219262306a36Sopenharmony_ci		dev_info(&dev->udev->dev, "Forcing UVC version to %u.%02x\n",
219362306a36Sopenharmony_ci			 dev->uvc_version >> 8, dev->uvc_version & 0xff);
219462306a36Sopenharmony_ci	}
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci	/* Register the V4L2 device. */
219762306a36Sopenharmony_ci	if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
219862306a36Sopenharmony_ci		goto error;
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	/* Scan the device for video chains. */
220162306a36Sopenharmony_ci	if (uvc_scan_device(dev) < 0)
220262306a36Sopenharmony_ci		goto error;
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci	/* Initialize controls. */
220562306a36Sopenharmony_ci	if (uvc_ctrl_init_device(dev) < 0)
220662306a36Sopenharmony_ci		goto error;
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	/* Register video device nodes. */
220962306a36Sopenharmony_ci	if (uvc_register_chains(dev) < 0)
221062306a36Sopenharmony_ci		goto error;
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
221362306a36Sopenharmony_ci	/* Register the media device node */
221462306a36Sopenharmony_ci	if (media_device_register(&dev->mdev) < 0)
221562306a36Sopenharmony_ci		goto error;
221662306a36Sopenharmony_ci#endif
221762306a36Sopenharmony_ci	/* Save our data pointer in the interface data. */
221862306a36Sopenharmony_ci	usb_set_intfdata(intf, dev);
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci	/* Initialize the interrupt URB. */
222162306a36Sopenharmony_ci	ret = uvc_status_init(dev);
222262306a36Sopenharmony_ci	if (ret < 0) {
222362306a36Sopenharmony_ci		dev_info(&dev->udev->dev,
222462306a36Sopenharmony_ci			 "Unable to initialize the status endpoint (%d), status interrupt will not be supported.\n",
222562306a36Sopenharmony_ci			 ret);
222662306a36Sopenharmony_ci	}
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci	ret = uvc_gpio_init_irq(dev);
222962306a36Sopenharmony_ci	if (ret < 0) {
223062306a36Sopenharmony_ci		dev_err(&dev->udev->dev,
223162306a36Sopenharmony_ci			"Unable to request privacy GPIO IRQ (%d)\n", ret);
223262306a36Sopenharmony_ci		goto error;
223362306a36Sopenharmony_ci	}
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_ci	uvc_dbg(dev, PROBE, "UVC device initialized\n");
223662306a36Sopenharmony_ci	usb_enable_autosuspend(udev);
223762306a36Sopenharmony_ci	return 0;
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_cierror:
224062306a36Sopenharmony_ci	uvc_unregister_video(dev);
224162306a36Sopenharmony_ci	kref_put(&dev->ref, uvc_delete);
224262306a36Sopenharmony_ci	return -ENODEV;
224362306a36Sopenharmony_ci}
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_cistatic void uvc_disconnect(struct usb_interface *intf)
224662306a36Sopenharmony_ci{
224762306a36Sopenharmony_ci	struct uvc_device *dev = usb_get_intfdata(intf);
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_ci	/*
225062306a36Sopenharmony_ci	 * Set the USB interface data to NULL. This can be done outside the
225162306a36Sopenharmony_ci	 * lock, as there's no other reader.
225262306a36Sopenharmony_ci	 */
225362306a36Sopenharmony_ci	usb_set_intfdata(intf, NULL);
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci	if (intf->cur_altsetting->desc.bInterfaceSubClass ==
225662306a36Sopenharmony_ci	    UVC_SC_VIDEOSTREAMING)
225762306a36Sopenharmony_ci		return;
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	uvc_unregister_video(dev);
226062306a36Sopenharmony_ci	kref_put(&dev->ref, uvc_delete);
226162306a36Sopenharmony_ci}
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_cistatic int uvc_suspend(struct usb_interface *intf, pm_message_t message)
226462306a36Sopenharmony_ci{
226562306a36Sopenharmony_ci	struct uvc_device *dev = usb_get_intfdata(intf);
226662306a36Sopenharmony_ci	struct uvc_streaming *stream;
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	uvc_dbg(dev, SUSPEND, "Suspending interface %u\n",
226962306a36Sopenharmony_ci		intf->cur_altsetting->desc.bInterfaceNumber);
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_ci	/* Controls are cached on the fly so they don't need to be saved. */
227262306a36Sopenharmony_ci	if (intf->cur_altsetting->desc.bInterfaceSubClass ==
227362306a36Sopenharmony_ci	    UVC_SC_VIDEOCONTROL) {
227462306a36Sopenharmony_ci		mutex_lock(&dev->lock);
227562306a36Sopenharmony_ci		if (dev->users)
227662306a36Sopenharmony_ci			uvc_status_stop(dev);
227762306a36Sopenharmony_ci		mutex_unlock(&dev->lock);
227862306a36Sopenharmony_ci		return 0;
227962306a36Sopenharmony_ci	}
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci	list_for_each_entry(stream, &dev->streams, list) {
228262306a36Sopenharmony_ci		if (stream->intf == intf)
228362306a36Sopenharmony_ci			return uvc_video_suspend(stream);
228462306a36Sopenharmony_ci	}
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci	uvc_dbg(dev, SUSPEND,
228762306a36Sopenharmony_ci		"Suspend: video streaming USB interface mismatch\n");
228862306a36Sopenharmony_ci	return -EINVAL;
228962306a36Sopenharmony_ci}
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_cistatic int __uvc_resume(struct usb_interface *intf, int reset)
229262306a36Sopenharmony_ci{
229362306a36Sopenharmony_ci	struct uvc_device *dev = usb_get_intfdata(intf);
229462306a36Sopenharmony_ci	struct uvc_streaming *stream;
229562306a36Sopenharmony_ci	int ret = 0;
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci	uvc_dbg(dev, SUSPEND, "Resuming interface %u\n",
229862306a36Sopenharmony_ci		intf->cur_altsetting->desc.bInterfaceNumber);
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci	if (intf->cur_altsetting->desc.bInterfaceSubClass ==
230162306a36Sopenharmony_ci	    UVC_SC_VIDEOCONTROL) {
230262306a36Sopenharmony_ci		if (reset) {
230362306a36Sopenharmony_ci			ret = uvc_ctrl_restore_values(dev);
230462306a36Sopenharmony_ci			if (ret < 0)
230562306a36Sopenharmony_ci				return ret;
230662306a36Sopenharmony_ci		}
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ci		mutex_lock(&dev->lock);
230962306a36Sopenharmony_ci		if (dev->users)
231062306a36Sopenharmony_ci			ret = uvc_status_start(dev, GFP_NOIO);
231162306a36Sopenharmony_ci		mutex_unlock(&dev->lock);
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci		return ret;
231462306a36Sopenharmony_ci	}
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci	list_for_each_entry(stream, &dev->streams, list) {
231762306a36Sopenharmony_ci		if (stream->intf == intf) {
231862306a36Sopenharmony_ci			ret = uvc_video_resume(stream, reset);
231962306a36Sopenharmony_ci			if (ret < 0)
232062306a36Sopenharmony_ci				uvc_queue_streamoff(&stream->queue,
232162306a36Sopenharmony_ci						    stream->queue.queue.type);
232262306a36Sopenharmony_ci			return ret;
232362306a36Sopenharmony_ci		}
232462306a36Sopenharmony_ci	}
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci	uvc_dbg(dev, SUSPEND,
232762306a36Sopenharmony_ci		"Resume: video streaming USB interface mismatch\n");
232862306a36Sopenharmony_ci	return -EINVAL;
232962306a36Sopenharmony_ci}
233062306a36Sopenharmony_ci
233162306a36Sopenharmony_cistatic int uvc_resume(struct usb_interface *intf)
233262306a36Sopenharmony_ci{
233362306a36Sopenharmony_ci	return __uvc_resume(intf, 0);
233462306a36Sopenharmony_ci}
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_cistatic int uvc_reset_resume(struct usb_interface *intf)
233762306a36Sopenharmony_ci{
233862306a36Sopenharmony_ci	return __uvc_resume(intf, 1);
233962306a36Sopenharmony_ci}
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci/* ------------------------------------------------------------------------
234262306a36Sopenharmony_ci * Module parameters
234362306a36Sopenharmony_ci */
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_cistatic int uvc_clock_param_get(char *buffer, const struct kernel_param *kp)
234662306a36Sopenharmony_ci{
234762306a36Sopenharmony_ci	if (uvc_clock_param == CLOCK_MONOTONIC)
234862306a36Sopenharmony_ci		return sprintf(buffer, "CLOCK_MONOTONIC");
234962306a36Sopenharmony_ci	else
235062306a36Sopenharmony_ci		return sprintf(buffer, "CLOCK_REALTIME");
235162306a36Sopenharmony_ci}
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_cistatic int uvc_clock_param_set(const char *val, const struct kernel_param *kp)
235462306a36Sopenharmony_ci{
235562306a36Sopenharmony_ci	if (strncasecmp(val, "clock_", strlen("clock_")) == 0)
235662306a36Sopenharmony_ci		val += strlen("clock_");
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_ci	if (strcasecmp(val, "monotonic") == 0)
235962306a36Sopenharmony_ci		uvc_clock_param = CLOCK_MONOTONIC;
236062306a36Sopenharmony_ci	else if (strcasecmp(val, "realtime") == 0)
236162306a36Sopenharmony_ci		uvc_clock_param = CLOCK_REALTIME;
236262306a36Sopenharmony_ci	else
236362306a36Sopenharmony_ci		return -EINVAL;
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	return 0;
236662306a36Sopenharmony_ci}
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_cimodule_param_call(clock, uvc_clock_param_set, uvc_clock_param_get,
236962306a36Sopenharmony_ci		  &uvc_clock_param, 0644);
237062306a36Sopenharmony_ciMODULE_PARM_DESC(clock, "Video buffers timestamp clock");
237162306a36Sopenharmony_cimodule_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, 0644);
237262306a36Sopenharmony_ciMODULE_PARM_DESC(hwtimestamps, "Use hardware timestamps");
237362306a36Sopenharmony_cimodule_param_named(nodrop, uvc_no_drop_param, uint, 0644);
237462306a36Sopenharmony_ciMODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
237562306a36Sopenharmony_cimodule_param_named(quirks, uvc_quirks_param, uint, 0644);
237662306a36Sopenharmony_ciMODULE_PARM_DESC(quirks, "Forced device quirks");
237762306a36Sopenharmony_cimodule_param_named(trace, uvc_dbg_param, uint, 0644);
237862306a36Sopenharmony_ciMODULE_PARM_DESC(trace, "Trace level bitmask");
237962306a36Sopenharmony_cimodule_param_named(timeout, uvc_timeout_param, uint, 0644);
238062306a36Sopenharmony_ciMODULE_PARM_DESC(timeout, "Streaming control requests timeout");
238162306a36Sopenharmony_ci
238262306a36Sopenharmony_ci/* ------------------------------------------------------------------------
238362306a36Sopenharmony_ci * Driver initialization and cleanup
238462306a36Sopenharmony_ci */
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_cistatic const struct uvc_device_info uvc_ctrl_power_line_limited = {
238762306a36Sopenharmony_ci	.mappings = (const struct uvc_control_mapping *[]) {
238862306a36Sopenharmony_ci		&uvc_ctrl_power_line_mapping_limited,
238962306a36Sopenharmony_ci		NULL, /* Sentinel */
239062306a36Sopenharmony_ci	},
239162306a36Sopenharmony_ci};
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_cistatic const struct uvc_device_info uvc_ctrl_power_line_uvc11 = {
239462306a36Sopenharmony_ci	.mappings = (const struct uvc_control_mapping *[]) {
239562306a36Sopenharmony_ci		&uvc_ctrl_power_line_mapping_uvc11,
239662306a36Sopenharmony_ci		NULL, /* Sentinel */
239762306a36Sopenharmony_ci	},
239862306a36Sopenharmony_ci};
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_probe_minmax = {
240162306a36Sopenharmony_ci	.quirks = UVC_QUIRK_PROBE_MINMAX,
240262306a36Sopenharmony_ci};
240362306a36Sopenharmony_ci
240462306a36Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_fix_bandwidth = {
240562306a36Sopenharmony_ci	.quirks = UVC_QUIRK_FIX_BANDWIDTH,
240662306a36Sopenharmony_ci};
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_probe_def = {
240962306a36Sopenharmony_ci	.quirks = UVC_QUIRK_PROBE_DEF,
241062306a36Sopenharmony_ci};
241162306a36Sopenharmony_ci
241262306a36Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_stream_no_fid = {
241362306a36Sopenharmony_ci	.quirks = UVC_QUIRK_STREAM_NO_FID,
241462306a36Sopenharmony_ci};
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_force_y8 = {
241762306a36Sopenharmony_ci	.quirks = UVC_QUIRK_FORCE_Y8,
241862306a36Sopenharmony_ci};
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci#define UVC_INFO_QUIRK(q) (kernel_ulong_t)&(struct uvc_device_info){.quirks = q}
242162306a36Sopenharmony_ci#define UVC_INFO_META(m) (kernel_ulong_t)&(struct uvc_device_info) \
242262306a36Sopenharmony_ci	{.meta_format = m}
242362306a36Sopenharmony_ci
242462306a36Sopenharmony_ci/*
242562306a36Sopenharmony_ci * The Logitech cameras listed below have their interface class set to
242662306a36Sopenharmony_ci * VENDOR_SPEC because they don't announce themselves as UVC devices, even
242762306a36Sopenharmony_ci * though they are compliant.
242862306a36Sopenharmony_ci */
242962306a36Sopenharmony_cistatic const struct usb_device_id uvc_ids[] = {
243062306a36Sopenharmony_ci	/* Quanta USB2.0 HD UVC Webcam */
243162306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
243262306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
243362306a36Sopenharmony_ci	  .idVendor		= 0x0408,
243462306a36Sopenharmony_ci	  .idProduct		= 0x3090,
243562306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
243662306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
243762306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
243862306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_ctrl_power_line_limited },
243962306a36Sopenharmony_ci	/* Quanta USB2.0 HD UVC Webcam */
244062306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
244162306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
244262306a36Sopenharmony_ci	  .idVendor		= 0x0408,
244362306a36Sopenharmony_ci	  .idProduct		= 0x4030,
244462306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
244562306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
244662306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
244762306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_ctrl_power_line_limited },
244862306a36Sopenharmony_ci	/* Quanta USB2.0 HD UVC Webcam */
244962306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
245062306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
245162306a36Sopenharmony_ci	  .idVendor		= 0x0408,
245262306a36Sopenharmony_ci	  .idProduct		= 0x4034,
245362306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
245462306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
245562306a36Sopenharmony_ci	  .bInterfaceProtocol	= UVC_PC_PROTOCOL_15,
245662306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_ctrl_power_line_limited },
245762306a36Sopenharmony_ci	/* LogiLink Wireless Webcam */
245862306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
245962306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
246062306a36Sopenharmony_ci	  .idVendor		= 0x0416,
246162306a36Sopenharmony_ci	  .idProduct		= 0xa91a,
246262306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
246362306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
246462306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
246562306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
246662306a36Sopenharmony_ci	/* Genius eFace 2025 */
246762306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
246862306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
246962306a36Sopenharmony_ci	  .idVendor		= 0x0458,
247062306a36Sopenharmony_ci	  .idProduct		= 0x706e,
247162306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
247262306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
247362306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
247462306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
247562306a36Sopenharmony_ci	/* Microsoft Lifecam NX-6000 */
247662306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
247762306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
247862306a36Sopenharmony_ci	  .idVendor		= 0x045e,
247962306a36Sopenharmony_ci	  .idProduct		= 0x00f8,
248062306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
248162306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
248262306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
248362306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
248462306a36Sopenharmony_ci	/* Microsoft Lifecam NX-3000 */
248562306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
248662306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
248762306a36Sopenharmony_ci	  .idVendor		= 0x045e,
248862306a36Sopenharmony_ci	  .idProduct		= 0x0721,
248962306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
249062306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
249162306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
249262306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_def },
249362306a36Sopenharmony_ci	/* Microsoft Lifecam VX-7000 */
249462306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
249562306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
249662306a36Sopenharmony_ci	  .idVendor		= 0x045e,
249762306a36Sopenharmony_ci	  .idProduct		= 0x0723,
249862306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
249962306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
250062306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
250162306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
250262306a36Sopenharmony_ci	/* Logitech, Webcam C910 */
250362306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
250462306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
250562306a36Sopenharmony_ci	  .idVendor		= 0x046d,
250662306a36Sopenharmony_ci	  .idProduct		= 0x0821,
250762306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
250862306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
250962306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
251062306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_WAKE_AUTOSUSPEND)},
251162306a36Sopenharmony_ci	/* Logitech, Webcam B910 */
251262306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
251362306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
251462306a36Sopenharmony_ci	  .idVendor		= 0x046d,
251562306a36Sopenharmony_ci	  .idProduct		= 0x0823,
251662306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
251762306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
251862306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
251962306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_WAKE_AUTOSUSPEND)},
252062306a36Sopenharmony_ci	/* Logitech Quickcam Fusion */
252162306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
252262306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
252362306a36Sopenharmony_ci	  .idVendor		= 0x046d,
252462306a36Sopenharmony_ci	  .idProduct		= 0x08c1,
252562306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
252662306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
252762306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0 },
252862306a36Sopenharmony_ci	/* Logitech Quickcam Orbit MP */
252962306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
253062306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
253162306a36Sopenharmony_ci	  .idVendor		= 0x046d,
253262306a36Sopenharmony_ci	  .idProduct		= 0x08c2,
253362306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
253462306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
253562306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0 },
253662306a36Sopenharmony_ci	/* Logitech Quickcam Pro for Notebook */
253762306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
253862306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
253962306a36Sopenharmony_ci	  .idVendor		= 0x046d,
254062306a36Sopenharmony_ci	  .idProduct		= 0x08c3,
254162306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
254262306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
254362306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0 },
254462306a36Sopenharmony_ci	/* Logitech Quickcam Pro 5000 */
254562306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
254662306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
254762306a36Sopenharmony_ci	  .idVendor		= 0x046d,
254862306a36Sopenharmony_ci	  .idProduct		= 0x08c5,
254962306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
255062306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
255162306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0 },
255262306a36Sopenharmony_ci	/* Logitech Quickcam OEM Dell Notebook */
255362306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
255462306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
255562306a36Sopenharmony_ci	  .idVendor		= 0x046d,
255662306a36Sopenharmony_ci	  .idProduct		= 0x08c6,
255762306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
255862306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
255962306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0 },
256062306a36Sopenharmony_ci	/* Logitech Quickcam OEM Cisco VT Camera II */
256162306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
256262306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
256362306a36Sopenharmony_ci	  .idVendor		= 0x046d,
256462306a36Sopenharmony_ci	  .idProduct		= 0x08c7,
256562306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
256662306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
256762306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0 },
256862306a36Sopenharmony_ci	/* Logitech HD Pro Webcam C920 */
256962306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
257062306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
257162306a36Sopenharmony_ci	  .idVendor		= 0x046d,
257262306a36Sopenharmony_ci	  .idProduct		= 0x082d,
257362306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
257462306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
257562306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
257662306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT) },
257762306a36Sopenharmony_ci	/* Chicony CNF7129 (Asus EEE 100HE) */
257862306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
257962306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
258062306a36Sopenharmony_ci	  .idVendor		= 0x04f2,
258162306a36Sopenharmony_ci	  .idProduct		= 0xb071,
258262306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
258362306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
258462306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
258562306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_RESTRICT_FRAME_RATE) },
258662306a36Sopenharmony_ci	/* Chicony EasyCamera */
258762306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
258862306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
258962306a36Sopenharmony_ci	  .idVendor		= 0x04f2,
259062306a36Sopenharmony_ci	  .idProduct		= 0xb5eb,
259162306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
259262306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
259362306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
259462306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_ctrl_power_line_limited },
259562306a36Sopenharmony_ci	/* Chicony Electronics Co., Ltd Integrated Camera */
259662306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
259762306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
259862306a36Sopenharmony_ci	  .idVendor		= 0x04f2,
259962306a36Sopenharmony_ci	  .idProduct		= 0xb67c,
260062306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
260162306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
260262306a36Sopenharmony_ci	  .bInterfaceProtocol	= UVC_PC_PROTOCOL_15,
260362306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 },
260462306a36Sopenharmony_ci	/* Chicony EasyCamera */
260562306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
260662306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
260762306a36Sopenharmony_ci	  .idVendor		= 0x04f2,
260862306a36Sopenharmony_ci	  .idProduct		= 0xb6ba,
260962306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
261062306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
261162306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
261262306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_ctrl_power_line_limited },
261362306a36Sopenharmony_ci	/* Chicony EasyCamera */
261462306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
261562306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
261662306a36Sopenharmony_ci	  .idVendor		= 0x04f2,
261762306a36Sopenharmony_ci	  .idProduct		= 0xb746,
261862306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
261962306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
262062306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
262162306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_ctrl_power_line_limited },
262262306a36Sopenharmony_ci	/* Alcor Micro AU3820 (Future Boy PC USB Webcam) */
262362306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
262462306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
262562306a36Sopenharmony_ci	  .idVendor		= 0x058f,
262662306a36Sopenharmony_ci	  .idProduct		= 0x3820,
262762306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
262862306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
262962306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
263062306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
263162306a36Sopenharmony_ci	/* Dell XPS m1530 */
263262306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
263362306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
263462306a36Sopenharmony_ci	  .idVendor		= 0x05a9,
263562306a36Sopenharmony_ci	  .idProduct		= 0x2640,
263662306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
263762306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
263862306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
263962306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_def },
264062306a36Sopenharmony_ci	/* Dell SP2008WFP Monitor */
264162306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
264262306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
264362306a36Sopenharmony_ci	  .idVendor		= 0x05a9,
264462306a36Sopenharmony_ci	  .idProduct		= 0x2641,
264562306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
264662306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
264762306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
264862306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_def },
264962306a36Sopenharmony_ci	/* Dell Alienware X51 */
265062306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
265162306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
265262306a36Sopenharmony_ci	  .idVendor		= 0x05a9,
265362306a36Sopenharmony_ci	  .idProduct		= 0x2643,
265462306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
265562306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
265662306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
265762306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_def },
265862306a36Sopenharmony_ci	/* Dell Studio Hybrid 140g (OmniVision webcam) */
265962306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
266062306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
266162306a36Sopenharmony_ci	  .idVendor		= 0x05a9,
266262306a36Sopenharmony_ci	  .idProduct		= 0x264a,
266362306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
266462306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
266562306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
266662306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_def },
266762306a36Sopenharmony_ci	/* Dell XPS M1330 (OmniVision OV7670 webcam) */
266862306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
266962306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
267062306a36Sopenharmony_ci	  .idVendor		= 0x05a9,
267162306a36Sopenharmony_ci	  .idProduct		= 0x7670,
267262306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
267362306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
267462306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
267562306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_def },
267662306a36Sopenharmony_ci	/* Apple Built-In iSight */
267762306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
267862306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
267962306a36Sopenharmony_ci	  .idVendor		= 0x05ac,
268062306a36Sopenharmony_ci	  .idProduct		= 0x8501,
268162306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
268262306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
268362306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
268462306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX
268562306a36Sopenharmony_ci					| UVC_QUIRK_BUILTIN_ISIGHT) },
268662306a36Sopenharmony_ci	/* Apple FaceTime HD Camera (Built-In) */
268762306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
268862306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
268962306a36Sopenharmony_ci	  .idVendor		= 0x05ac,
269062306a36Sopenharmony_ci	  .idProduct		= 0x8514,
269162306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
269262306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
269362306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
269462306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_def },
269562306a36Sopenharmony_ci	/* Apple Built-In iSight via iBridge */
269662306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
269762306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
269862306a36Sopenharmony_ci	  .idVendor		= 0x05ac,
269962306a36Sopenharmony_ci	  .idProduct		= 0x8600,
270062306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
270162306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
270262306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
270362306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_def },
270462306a36Sopenharmony_ci	/* Foxlink ("HP Webcam" on HP Mini 5103) */
270562306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
270662306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
270762306a36Sopenharmony_ci	  .idVendor		= 0x05c8,
270862306a36Sopenharmony_ci	  .idProduct		= 0x0403,
270962306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
271062306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
271162306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
271262306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_fix_bandwidth },
271362306a36Sopenharmony_ci	/* Genesys Logic USB 2.0 PC Camera */
271462306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
271562306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
271662306a36Sopenharmony_ci	  .idVendor		= 0x05e3,
271762306a36Sopenharmony_ci	  .idProduct		= 0x0505,
271862306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
271962306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
272062306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
272162306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
272262306a36Sopenharmony_ci	/* Hercules Classic Silver */
272362306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
272462306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
272562306a36Sopenharmony_ci	  .idVendor		= 0x06f8,
272662306a36Sopenharmony_ci	  .idProduct		= 0x300c,
272762306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
272862306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
272962306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
273062306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_fix_bandwidth },
273162306a36Sopenharmony_ci	/* ViMicro Vega */
273262306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
273362306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
273462306a36Sopenharmony_ci	  .idVendor		= 0x0ac8,
273562306a36Sopenharmony_ci	  .idProduct		= 0x332d,
273662306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
273762306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
273862306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
273962306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_fix_bandwidth },
274062306a36Sopenharmony_ci	/* ViMicro - Minoru3D */
274162306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
274262306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
274362306a36Sopenharmony_ci	  .idVendor		= 0x0ac8,
274462306a36Sopenharmony_ci	  .idProduct		= 0x3410,
274562306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
274662306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
274762306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
274862306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_fix_bandwidth },
274962306a36Sopenharmony_ci	/* ViMicro Venus - Minoru3D */
275062306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
275162306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
275262306a36Sopenharmony_ci	  .idVendor		= 0x0ac8,
275362306a36Sopenharmony_ci	  .idProduct		= 0x3420,
275462306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
275562306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
275662306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
275762306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_fix_bandwidth },
275862306a36Sopenharmony_ci	/* Ophir Optronics - SPCAM 620U */
275962306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
276062306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
276162306a36Sopenharmony_ci	  .idVendor		= 0x0bd3,
276262306a36Sopenharmony_ci	  .idProduct		= 0x0555,
276362306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
276462306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
276562306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
276662306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
276762306a36Sopenharmony_ci	/* MT6227 */
276862306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
276962306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
277062306a36Sopenharmony_ci	  .idVendor		= 0x0e8d,
277162306a36Sopenharmony_ci	  .idProduct		= 0x0004,
277262306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
277362306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
277462306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
277562306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX
277662306a36Sopenharmony_ci					| UVC_QUIRK_PROBE_DEF) },
277762306a36Sopenharmony_ci	/* IMC Networks (Medion Akoya) */
277862306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
277962306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
278062306a36Sopenharmony_ci	  .idVendor		= 0x13d3,
278162306a36Sopenharmony_ci	  .idProduct		= 0x5103,
278262306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
278362306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
278462306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
278562306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
278662306a36Sopenharmony_ci	/* JMicron USB2.0 XGA WebCam */
278762306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
278862306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
278962306a36Sopenharmony_ci	  .idVendor		= 0x152d,
279062306a36Sopenharmony_ci	  .idProduct		= 0x0310,
279162306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
279262306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
279362306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
279462306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
279562306a36Sopenharmony_ci	/* Syntek (HP Spartan) */
279662306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
279762306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
279862306a36Sopenharmony_ci	  .idVendor		= 0x174f,
279962306a36Sopenharmony_ci	  .idProduct		= 0x5212,
280062306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
280162306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
280262306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
280362306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
280462306a36Sopenharmony_ci	/* Syntek (Samsung Q310) */
280562306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
280662306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
280762306a36Sopenharmony_ci	  .idVendor		= 0x174f,
280862306a36Sopenharmony_ci	  .idProduct		= 0x5931,
280962306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
281062306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
281162306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
281262306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
281362306a36Sopenharmony_ci	/* Syntek (Packard Bell EasyNote MX52 */
281462306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
281562306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
281662306a36Sopenharmony_ci	  .idVendor		= 0x174f,
281762306a36Sopenharmony_ci	  .idProduct		= 0x8a12,
281862306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
281962306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
282062306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
282162306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
282262306a36Sopenharmony_ci	/* Syntek (Asus F9SG) */
282362306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
282462306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
282562306a36Sopenharmony_ci	  .idVendor		= 0x174f,
282662306a36Sopenharmony_ci	  .idProduct		= 0x8a31,
282762306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
282862306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
282962306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
283062306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
283162306a36Sopenharmony_ci	/* Syntek (Asus U3S) */
283262306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
283362306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
283462306a36Sopenharmony_ci	  .idVendor		= 0x174f,
283562306a36Sopenharmony_ci	  .idProduct		= 0x8a33,
283662306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
283762306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
283862306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
283962306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
284062306a36Sopenharmony_ci	/* Syntek (JAOtech Smart Terminal) */
284162306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
284262306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
284362306a36Sopenharmony_ci	  .idVendor		= 0x174f,
284462306a36Sopenharmony_ci	  .idProduct		= 0x8a34,
284562306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
284662306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
284762306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
284862306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
284962306a36Sopenharmony_ci	/* Miricle 307K */
285062306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
285162306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
285262306a36Sopenharmony_ci	  .idVendor		= 0x17dc,
285362306a36Sopenharmony_ci	  .idProduct		= 0x0202,
285462306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
285562306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
285662306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
285762306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
285862306a36Sopenharmony_ci	/* Lenovo Thinkpad SL400/SL500 */
285962306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
286062306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
286162306a36Sopenharmony_ci	  .idVendor		= 0x17ef,
286262306a36Sopenharmony_ci	  .idProduct		= 0x480b,
286362306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
286462306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
286562306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
286662306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
286762306a36Sopenharmony_ci	/* Aveo Technology USB 2.0 Camera */
286862306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
286962306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
287062306a36Sopenharmony_ci	  .idVendor		= 0x1871,
287162306a36Sopenharmony_ci	  .idProduct		= 0x0306,
287262306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
287362306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
287462306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
287562306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX
287662306a36Sopenharmony_ci					| UVC_QUIRK_PROBE_EXTRAFIELDS) },
287762306a36Sopenharmony_ci	/* Aveo Technology USB 2.0 Camera (Tasco USB Microscope) */
287862306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
287962306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
288062306a36Sopenharmony_ci	  .idVendor		= 0x1871,
288162306a36Sopenharmony_ci	  .idProduct		= 0x0516,
288262306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
288362306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
288462306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0 },
288562306a36Sopenharmony_ci	/* Ecamm Pico iMage */
288662306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
288762306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
288862306a36Sopenharmony_ci	  .idVendor		= 0x18cd,
288962306a36Sopenharmony_ci	  .idProduct		= 0xcafe,
289062306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
289162306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
289262306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
289362306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_PROBE_EXTRAFIELDS) },
289462306a36Sopenharmony_ci	/* Manta MM-353 Plako */
289562306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
289662306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
289762306a36Sopenharmony_ci	  .idVendor		= 0x18ec,
289862306a36Sopenharmony_ci	  .idProduct		= 0x3188,
289962306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
290062306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
290162306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
290262306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
290362306a36Sopenharmony_ci	/* FSC WebCam V30S */
290462306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
290562306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
290662306a36Sopenharmony_ci	  .idVendor		= 0x18ec,
290762306a36Sopenharmony_ci	  .idProduct		= 0x3288,
290862306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
290962306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
291062306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
291162306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
291262306a36Sopenharmony_ci	/* Arkmicro unbranded */
291362306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
291462306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
291562306a36Sopenharmony_ci	  .idVendor		= 0x18ec,
291662306a36Sopenharmony_ci	  .idProduct		= 0x3290,
291762306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
291862306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
291962306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
292062306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_def },
292162306a36Sopenharmony_ci	/* The Imaging Source USB CCD cameras */
292262306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
292362306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
292462306a36Sopenharmony_ci	  .idVendor		= 0x199e,
292562306a36Sopenharmony_ci	  .idProduct		= 0x8102,
292662306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
292762306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
292862306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0 },
292962306a36Sopenharmony_ci	/* Bodelin ProScopeHR */
293062306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
293162306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_DEV_HI
293262306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
293362306a36Sopenharmony_ci	  .idVendor		= 0x19ab,
293462306a36Sopenharmony_ci	  .idProduct		= 0x1000,
293562306a36Sopenharmony_ci	  .bcdDevice_hi		= 0x0126,
293662306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
293762306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
293862306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
293962306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_STATUS_INTERVAL) },
294062306a36Sopenharmony_ci	/* MSI StarCam 370i */
294162306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
294262306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
294362306a36Sopenharmony_ci	  .idVendor		= 0x1b3b,
294462306a36Sopenharmony_ci	  .idProduct		= 0x2951,
294562306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
294662306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
294762306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
294862306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
294962306a36Sopenharmony_ci	/* Generalplus Technology Inc. 808 Camera */
295062306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
295162306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
295262306a36Sopenharmony_ci	  .idVendor		= 0x1b3f,
295362306a36Sopenharmony_ci	  .idProduct		= 0x2002,
295462306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
295562306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
295662306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
295762306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
295862306a36Sopenharmony_ci	/* Shenzhen Aoni Electronic Co.,Ltd 2K FHD camera */
295962306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
296062306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
296162306a36Sopenharmony_ci	  .idVendor		= 0x1bcf,
296262306a36Sopenharmony_ci	  .idProduct		= 0x0b40,
296362306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
296462306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
296562306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
296662306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&(const struct uvc_device_info){
296762306a36Sopenharmony_ci		.uvc_version = 0x010a,
296862306a36Sopenharmony_ci	  } },
296962306a36Sopenharmony_ci	/* SiGma Micro USB Web Camera */
297062306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
297162306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
297262306a36Sopenharmony_ci	  .idVendor		= 0x1c4f,
297362306a36Sopenharmony_ci	  .idProduct		= 0x3000,
297462306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
297562306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
297662306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
297762306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX
297862306a36Sopenharmony_ci					| UVC_QUIRK_IGNORE_SELECTOR_UNIT) },
297962306a36Sopenharmony_ci	/* Oculus VR Positional Tracker DK2 */
298062306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
298162306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
298262306a36Sopenharmony_ci	  .idVendor		= 0x2833,
298362306a36Sopenharmony_ci	  .idProduct		= 0x0201,
298462306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
298562306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
298662306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
298762306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_force_y8 },
298862306a36Sopenharmony_ci	/* Oculus VR Rift Sensor */
298962306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
299062306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
299162306a36Sopenharmony_ci	  .idVendor		= 0x2833,
299262306a36Sopenharmony_ci	  .idProduct		= 0x0211,
299362306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
299462306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
299562306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
299662306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_force_y8 },
299762306a36Sopenharmony_ci	/* GEO Semiconductor GC6500 */
299862306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
299962306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
300062306a36Sopenharmony_ci	  .idVendor		= 0x29fe,
300162306a36Sopenharmony_ci	  .idProduct		= 0x4d53,
300262306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
300362306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
300462306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
300562306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_FORCE_BPP) },
300662306a36Sopenharmony_ci	/* SunplusIT Inc HD Camera */
300762306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
300862306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
300962306a36Sopenharmony_ci	  .idVendor		= 0x2b7e,
301062306a36Sopenharmony_ci	  .idProduct		= 0xb752,
301162306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
301262306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
301362306a36Sopenharmony_ci	  .bInterfaceProtocol	= UVC_PC_PROTOCOL_15,
301462306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 },
301562306a36Sopenharmony_ci	/* Lenovo Integrated Camera */
301662306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
301762306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
301862306a36Sopenharmony_ci	  .idVendor		= 0x30c9,
301962306a36Sopenharmony_ci	  .idProduct		= 0x0093,
302062306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
302162306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
302262306a36Sopenharmony_ci	  .bInterfaceProtocol	= UVC_PC_PROTOCOL_15,
302362306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 },
302462306a36Sopenharmony_ci	/* Sonix Technology USB 2.0 Camera */
302562306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
302662306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
302762306a36Sopenharmony_ci	  .idVendor		= 0x3277,
302862306a36Sopenharmony_ci	  .idProduct		= 0x0072,
302962306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
303062306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
303162306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
303262306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_ctrl_power_line_limited },
303362306a36Sopenharmony_ci	/* Acer EasyCamera */
303462306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
303562306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
303662306a36Sopenharmony_ci	  .idVendor		= 0x5986,
303762306a36Sopenharmony_ci	  .idProduct		= 0x1172,
303862306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
303962306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
304062306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
304162306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_ctrl_power_line_limited },
304262306a36Sopenharmony_ci	/* Acer EasyCamera */
304362306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
304462306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
304562306a36Sopenharmony_ci	  .idVendor		= 0x5986,
304662306a36Sopenharmony_ci	  .idProduct		= 0x1180,
304762306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
304862306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
304962306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
305062306a36Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_ctrl_power_line_limited },
305162306a36Sopenharmony_ci	/* Intel D410/ASR depth camera */
305262306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
305362306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
305462306a36Sopenharmony_ci	  .idVendor		= 0x8086,
305562306a36Sopenharmony_ci	  .idProduct		= 0x0ad2,
305662306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
305762306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
305862306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
305962306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_META(V4L2_META_FMT_D4XX) },
306062306a36Sopenharmony_ci	/* Intel D415/ASRC depth camera */
306162306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
306262306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
306362306a36Sopenharmony_ci	  .idVendor		= 0x8086,
306462306a36Sopenharmony_ci	  .idProduct		= 0x0ad3,
306562306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
306662306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
306762306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
306862306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_META(V4L2_META_FMT_D4XX) },
306962306a36Sopenharmony_ci	/* Intel D430/AWG depth camera */
307062306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
307162306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
307262306a36Sopenharmony_ci	  .idVendor		= 0x8086,
307362306a36Sopenharmony_ci	  .idProduct		= 0x0ad4,
307462306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
307562306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
307662306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
307762306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_META(V4L2_META_FMT_D4XX) },
307862306a36Sopenharmony_ci	/* Intel RealSense D4M */
307962306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
308062306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
308162306a36Sopenharmony_ci	  .idVendor		= 0x8086,
308262306a36Sopenharmony_ci	  .idProduct		= 0x0b03,
308362306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
308462306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
308562306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
308662306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_META(V4L2_META_FMT_D4XX) },
308762306a36Sopenharmony_ci	/* Intel D435/AWGC depth camera */
308862306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
308962306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
309062306a36Sopenharmony_ci	  .idVendor		= 0x8086,
309162306a36Sopenharmony_ci	  .idProduct		= 0x0b07,
309262306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
309362306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
309462306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
309562306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_META(V4L2_META_FMT_D4XX) },
309662306a36Sopenharmony_ci	/* Intel D435i depth camera */
309762306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
309862306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
309962306a36Sopenharmony_ci	  .idVendor		= 0x8086,
310062306a36Sopenharmony_ci	  .idProduct		= 0x0b3a,
310162306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
310262306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
310362306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
310462306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_META(V4L2_META_FMT_D4XX) },
310562306a36Sopenharmony_ci	/* Intel D405 Depth Camera */
310662306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
310762306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
310862306a36Sopenharmony_ci	  .idVendor		= 0x8086,
310962306a36Sopenharmony_ci	  .idProduct		= 0x0b5b,
311062306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
311162306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
311262306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
311362306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_META(V4L2_META_FMT_D4XX) },
311462306a36Sopenharmony_ci	/* Intel D455 Depth Camera */
311562306a36Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
311662306a36Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
311762306a36Sopenharmony_ci	  .idVendor		= 0x8086,
311862306a36Sopenharmony_ci	  .idProduct		= 0x0b5c,
311962306a36Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
312062306a36Sopenharmony_ci	  .bInterfaceSubClass	= 1,
312162306a36Sopenharmony_ci	  .bInterfaceProtocol	= 0,
312262306a36Sopenharmony_ci	  .driver_info		= UVC_INFO_META(V4L2_META_FMT_D4XX) },
312362306a36Sopenharmony_ci	/* Generic USB Video Class */
312462306a36Sopenharmony_ci	{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_UNDEFINED) },
312562306a36Sopenharmony_ci	{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_15) },
312662306a36Sopenharmony_ci	{}
312762306a36Sopenharmony_ci};
312862306a36Sopenharmony_ci
312962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, uvc_ids);
313062306a36Sopenharmony_ci
313162306a36Sopenharmony_cistruct uvc_driver uvc_driver = {
313262306a36Sopenharmony_ci	.driver = {
313362306a36Sopenharmony_ci		.name		= "uvcvideo",
313462306a36Sopenharmony_ci		.probe		= uvc_probe,
313562306a36Sopenharmony_ci		.disconnect	= uvc_disconnect,
313662306a36Sopenharmony_ci		.suspend	= uvc_suspend,
313762306a36Sopenharmony_ci		.resume		= uvc_resume,
313862306a36Sopenharmony_ci		.reset_resume	= uvc_reset_resume,
313962306a36Sopenharmony_ci		.id_table	= uvc_ids,
314062306a36Sopenharmony_ci		.supports_autosuspend = 1,
314162306a36Sopenharmony_ci	},
314262306a36Sopenharmony_ci};
314362306a36Sopenharmony_ci
314462306a36Sopenharmony_cistatic int __init uvc_init(void)
314562306a36Sopenharmony_ci{
314662306a36Sopenharmony_ci	int ret;
314762306a36Sopenharmony_ci
314862306a36Sopenharmony_ci	uvc_debugfs_init();
314962306a36Sopenharmony_ci
315062306a36Sopenharmony_ci	ret = usb_register(&uvc_driver.driver);
315162306a36Sopenharmony_ci	if (ret < 0) {
315262306a36Sopenharmony_ci		uvc_debugfs_cleanup();
315362306a36Sopenharmony_ci		return ret;
315462306a36Sopenharmony_ci	}
315562306a36Sopenharmony_ci
315662306a36Sopenharmony_ci	return 0;
315762306a36Sopenharmony_ci}
315862306a36Sopenharmony_ci
315962306a36Sopenharmony_cistatic void __exit uvc_cleanup(void)
316062306a36Sopenharmony_ci{
316162306a36Sopenharmony_ci	usb_deregister(&uvc_driver.driver);
316262306a36Sopenharmony_ci	uvc_debugfs_cleanup();
316362306a36Sopenharmony_ci}
316462306a36Sopenharmony_ci
316562306a36Sopenharmony_cimodule_init(uvc_init);
316662306a36Sopenharmony_cimodule_exit(uvc_cleanup);
316762306a36Sopenharmony_ci
316862306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
316962306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
317062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
317162306a36Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION);
317262306a36Sopenharmony_ci
3173