18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *      uvc_driver.c  --  USB Video Class driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *      Copyright (C) 2005-2010
68c2ecf20Sopenharmony_ci *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/atomic.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/list.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/usb.h>
158c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
168c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
178c2ecf20Sopenharmony_ci#include <linux/wait.h>
188c2ecf20Sopenharmony_ci#include <linux/version.h>
198c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <media/v4l2-common.h>
228c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "uvcvideo.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR		"Laurent Pinchart " \
278c2ecf20Sopenharmony_ci				"<laurent.pinchart@ideasonboard.com>"
288c2ecf20Sopenharmony_ci#define DRIVER_DESC		"USB Video Class driver"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ciunsigned int uvc_clock_param = CLOCK_MONOTONIC;
318c2ecf20Sopenharmony_ciunsigned int uvc_hw_timestamps_param;
328c2ecf20Sopenharmony_ciunsigned int uvc_no_drop_param;
338c2ecf20Sopenharmony_cistatic unsigned int uvc_quirks_param = -1;
348c2ecf20Sopenharmony_ciunsigned int uvc_trace_param;
358c2ecf20Sopenharmony_ciunsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------
388c2ecf20Sopenharmony_ci * Video formats
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic struct uvc_format_desc uvc_fmts[] = {
428c2ecf20Sopenharmony_ci	{
438c2ecf20Sopenharmony_ci		.name		= "YUV 4:2:2 (YUYV)",
448c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_YUY2,
458c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_YUYV,
468c2ecf20Sopenharmony_ci	},
478c2ecf20Sopenharmony_ci	{
488c2ecf20Sopenharmony_ci		.name		= "YUV 4:2:2 (YUYV)",
498c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_YUY2_ISIGHT,
508c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_YUYV,
518c2ecf20Sopenharmony_ci	},
528c2ecf20Sopenharmony_ci	{
538c2ecf20Sopenharmony_ci		.name		= "YUV 4:2:0 (NV12)",
548c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_NV12,
558c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_NV12,
568c2ecf20Sopenharmony_ci	},
578c2ecf20Sopenharmony_ci	{
588c2ecf20Sopenharmony_ci		.name		= "MJPEG",
598c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_MJPEG,
608c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_MJPEG,
618c2ecf20Sopenharmony_ci	},
628c2ecf20Sopenharmony_ci	{
638c2ecf20Sopenharmony_ci		.name		= "YVU 4:2:0 (YV12)",
648c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_YV12,
658c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_YVU420,
668c2ecf20Sopenharmony_ci	},
678c2ecf20Sopenharmony_ci	{
688c2ecf20Sopenharmony_ci		.name		= "YUV 4:2:0 (I420)",
698c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_I420,
708c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_YUV420,
718c2ecf20Sopenharmony_ci	},
728c2ecf20Sopenharmony_ci	{
738c2ecf20Sopenharmony_ci		.name		= "YUV 4:2:0 (M420)",
748c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_M420,
758c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_M420,
768c2ecf20Sopenharmony_ci	},
778c2ecf20Sopenharmony_ci	{
788c2ecf20Sopenharmony_ci		.name		= "YUV 4:2:2 (UYVY)",
798c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_UYVY,
808c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_UYVY,
818c2ecf20Sopenharmony_ci	},
828c2ecf20Sopenharmony_ci	{
838c2ecf20Sopenharmony_ci		.name		= "Greyscale 8-bit (Y800)",
848c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_Y800,
858c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_GREY,
868c2ecf20Sopenharmony_ci	},
878c2ecf20Sopenharmony_ci	{
888c2ecf20Sopenharmony_ci		.name		= "Greyscale 8-bit (Y8  )",
898c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_Y8,
908c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_GREY,
918c2ecf20Sopenharmony_ci	},
928c2ecf20Sopenharmony_ci	{
938c2ecf20Sopenharmony_ci		.name		= "Greyscale 8-bit (D3DFMT_L8)",
948c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_D3DFMT_L8,
958c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_GREY,
968c2ecf20Sopenharmony_ci	},
978c2ecf20Sopenharmony_ci	{
988c2ecf20Sopenharmony_ci		.name		= "IR 8-bit (L8_IR)",
998c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_KSMEDIA_L8_IR,
1008c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_GREY,
1018c2ecf20Sopenharmony_ci	},
1028c2ecf20Sopenharmony_ci	{
1038c2ecf20Sopenharmony_ci		.name		= "Greyscale 10-bit (Y10 )",
1048c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_Y10,
1058c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_Y10,
1068c2ecf20Sopenharmony_ci	},
1078c2ecf20Sopenharmony_ci	{
1088c2ecf20Sopenharmony_ci		.name		= "Greyscale 12-bit (Y12 )",
1098c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_Y12,
1108c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_Y12,
1118c2ecf20Sopenharmony_ci	},
1128c2ecf20Sopenharmony_ci	{
1138c2ecf20Sopenharmony_ci		.name		= "Greyscale 16-bit (Y16 )",
1148c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_Y16,
1158c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_Y16,
1168c2ecf20Sopenharmony_ci	},
1178c2ecf20Sopenharmony_ci	{
1188c2ecf20Sopenharmony_ci		.name		= "BGGR Bayer (BY8 )",
1198c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_BY8,
1208c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_SBGGR8,
1218c2ecf20Sopenharmony_ci	},
1228c2ecf20Sopenharmony_ci	{
1238c2ecf20Sopenharmony_ci		.name		= "BGGR Bayer (BA81)",
1248c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_BA81,
1258c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_SBGGR8,
1268c2ecf20Sopenharmony_ci	},
1278c2ecf20Sopenharmony_ci	{
1288c2ecf20Sopenharmony_ci		.name		= "GBRG Bayer (GBRG)",
1298c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_GBRG,
1308c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_SGBRG8,
1318c2ecf20Sopenharmony_ci	},
1328c2ecf20Sopenharmony_ci	{
1338c2ecf20Sopenharmony_ci		.name		= "GRBG Bayer (GRBG)",
1348c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_GRBG,
1358c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_SGRBG8,
1368c2ecf20Sopenharmony_ci	},
1378c2ecf20Sopenharmony_ci	{
1388c2ecf20Sopenharmony_ci		.name		= "RGGB Bayer (RGGB)",
1398c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_RGGB,
1408c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_SRGGB8,
1418c2ecf20Sopenharmony_ci	},
1428c2ecf20Sopenharmony_ci	{
1438c2ecf20Sopenharmony_ci		.name		= "RGB565",
1448c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_RGBP,
1458c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_RGB565,
1468c2ecf20Sopenharmony_ci	},
1478c2ecf20Sopenharmony_ci	{
1488c2ecf20Sopenharmony_ci		.name		= "BGR 8:8:8 (BGR3)",
1498c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_BGR3,
1508c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_BGR24,
1518c2ecf20Sopenharmony_ci	},
1528c2ecf20Sopenharmony_ci	{
1538c2ecf20Sopenharmony_ci		.name		= "H.264",
1548c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_H264,
1558c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_H264,
1568c2ecf20Sopenharmony_ci	},
1578c2ecf20Sopenharmony_ci	{
1588c2ecf20Sopenharmony_ci		.name		= "Greyscale 8 L/R (Y8I)",
1598c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_Y8I,
1608c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_Y8I,
1618c2ecf20Sopenharmony_ci	},
1628c2ecf20Sopenharmony_ci	{
1638c2ecf20Sopenharmony_ci		.name		= "Greyscale 12 L/R (Y12I)",
1648c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_Y12I,
1658c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_Y12I,
1668c2ecf20Sopenharmony_ci	},
1678c2ecf20Sopenharmony_ci	{
1688c2ecf20Sopenharmony_ci		.name		= "Depth data 16-bit (Z16)",
1698c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_Z16,
1708c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_Z16,
1718c2ecf20Sopenharmony_ci	},
1728c2ecf20Sopenharmony_ci	{
1738c2ecf20Sopenharmony_ci		.name		= "Bayer 10-bit (SRGGB10P)",
1748c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_RW10,
1758c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_SRGGB10P,
1768c2ecf20Sopenharmony_ci	},
1778c2ecf20Sopenharmony_ci	{
1788c2ecf20Sopenharmony_ci		.name		= "Bayer 16-bit (SBGGR16)",
1798c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_BG16,
1808c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_SBGGR16,
1818c2ecf20Sopenharmony_ci	},
1828c2ecf20Sopenharmony_ci	{
1838c2ecf20Sopenharmony_ci		.name		= "Bayer 16-bit (SGBRG16)",
1848c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_GB16,
1858c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_SGBRG16,
1868c2ecf20Sopenharmony_ci	},
1878c2ecf20Sopenharmony_ci	{
1888c2ecf20Sopenharmony_ci		.name		= "Bayer 16-bit (SRGGB16)",
1898c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_RG16,
1908c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_SRGGB16,
1918c2ecf20Sopenharmony_ci	},
1928c2ecf20Sopenharmony_ci	{
1938c2ecf20Sopenharmony_ci		.name		= "Bayer 16-bit (SGRBG16)",
1948c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_GR16,
1958c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_SGRBG16,
1968c2ecf20Sopenharmony_ci	},
1978c2ecf20Sopenharmony_ci	{
1988c2ecf20Sopenharmony_ci		.name		= "Depth data 16-bit (Z16)",
1998c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_INVZ,
2008c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_Z16,
2018c2ecf20Sopenharmony_ci	},
2028c2ecf20Sopenharmony_ci	{
2038c2ecf20Sopenharmony_ci		.name		= "Greyscale 10-bit (Y10 )",
2048c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_INVI,
2058c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_Y10,
2068c2ecf20Sopenharmony_ci	},
2078c2ecf20Sopenharmony_ci	{
2088c2ecf20Sopenharmony_ci		.name		= "IR:Depth 26-bit (INZI)",
2098c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_INZI,
2108c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_INZI,
2118c2ecf20Sopenharmony_ci	},
2128c2ecf20Sopenharmony_ci	{
2138c2ecf20Sopenharmony_ci		.name		= "4-bit Depth Confidence (Packed)",
2148c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_CNF4,
2158c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_CNF4,
2168c2ecf20Sopenharmony_ci	},
2178c2ecf20Sopenharmony_ci	{
2188c2ecf20Sopenharmony_ci		.name		= "HEVC",
2198c2ecf20Sopenharmony_ci		.guid		= UVC_GUID_FORMAT_HEVC,
2208c2ecf20Sopenharmony_ci		.fcc		= V4L2_PIX_FMT_HEVC,
2218c2ecf20Sopenharmony_ci	},
2228c2ecf20Sopenharmony_ci};
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------
2258c2ecf20Sopenharmony_ci * Utility functions
2268c2ecf20Sopenharmony_ci */
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistruct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
2298c2ecf20Sopenharmony_ci		u8 epaddr)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	struct usb_host_endpoint *ep;
2328c2ecf20Sopenharmony_ci	unsigned int i;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
2358c2ecf20Sopenharmony_ci		ep = &alts->endpoint[i];
2368c2ecf20Sopenharmony_ci		if (ep->desc.bEndpointAddress == epaddr)
2378c2ecf20Sopenharmony_ci			return ep;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return NULL;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16])
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	unsigned int len = ARRAY_SIZE(uvc_fmts);
2468c2ecf20Sopenharmony_ci	unsigned int i;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	for (i = 0; i < len; ++i) {
2498c2ecf20Sopenharmony_ci		if (memcmp(guid, uvc_fmts[i].guid, 16) == 0)
2508c2ecf20Sopenharmony_ci			return &uvc_fmts[i];
2518c2ecf20Sopenharmony_ci	}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	return NULL;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic enum v4l2_colorspace uvc_colorspace(const u8 primaries)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	static const enum v4l2_colorspace colorprimaries[] = {
2598c2ecf20Sopenharmony_ci		V4L2_COLORSPACE_DEFAULT,  /* Unspecified */
2608c2ecf20Sopenharmony_ci		V4L2_COLORSPACE_SRGB,
2618c2ecf20Sopenharmony_ci		V4L2_COLORSPACE_470_SYSTEM_M,
2628c2ecf20Sopenharmony_ci		V4L2_COLORSPACE_470_SYSTEM_BG,
2638c2ecf20Sopenharmony_ci		V4L2_COLORSPACE_SMPTE170M,
2648c2ecf20Sopenharmony_ci		V4L2_COLORSPACE_SMPTE240M,
2658c2ecf20Sopenharmony_ci	};
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (primaries < ARRAY_SIZE(colorprimaries))
2688c2ecf20Sopenharmony_ci		return colorprimaries[primaries];
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	return V4L2_COLORSPACE_DEFAULT;  /* Reserved */
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic enum v4l2_xfer_func uvc_xfer_func(const u8 transfer_characteristics)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	/*
2768c2ecf20Sopenharmony_ci	 * V4L2 does not currently have definitions for all possible values of
2778c2ecf20Sopenharmony_ci	 * UVC transfer characteristics. If v4l2_xfer_func is extended with new
2788c2ecf20Sopenharmony_ci	 * values, the mapping below should be updated.
2798c2ecf20Sopenharmony_ci	 *
2808c2ecf20Sopenharmony_ci	 * Substitutions are taken from the mapping given for
2818c2ecf20Sopenharmony_ci	 * V4L2_XFER_FUNC_DEFAULT documented in videodev2.h.
2828c2ecf20Sopenharmony_ci	 */
2838c2ecf20Sopenharmony_ci	static const enum v4l2_xfer_func xfer_funcs[] = {
2848c2ecf20Sopenharmony_ci		V4L2_XFER_FUNC_DEFAULT,    /* Unspecified */
2858c2ecf20Sopenharmony_ci		V4L2_XFER_FUNC_709,
2868c2ecf20Sopenharmony_ci		V4L2_XFER_FUNC_709,        /* Substitution for BT.470-2 M */
2878c2ecf20Sopenharmony_ci		V4L2_XFER_FUNC_709,        /* Substitution for BT.470-2 B, G */
2888c2ecf20Sopenharmony_ci		V4L2_XFER_FUNC_709,        /* Substitution for SMPTE 170M */
2898c2ecf20Sopenharmony_ci		V4L2_XFER_FUNC_SMPTE240M,
2908c2ecf20Sopenharmony_ci		V4L2_XFER_FUNC_NONE,
2918c2ecf20Sopenharmony_ci		V4L2_XFER_FUNC_SRGB,
2928c2ecf20Sopenharmony_ci	};
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	if (transfer_characteristics < ARRAY_SIZE(xfer_funcs))
2958c2ecf20Sopenharmony_ci		return xfer_funcs[transfer_characteristics];
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return V4L2_XFER_FUNC_DEFAULT;  /* Reserved */
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic enum v4l2_ycbcr_encoding uvc_ycbcr_enc(const u8 matrix_coefficients)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	/*
3038c2ecf20Sopenharmony_ci	 * V4L2 does not currently have definitions for all possible values of
3048c2ecf20Sopenharmony_ci	 * UVC matrix coefficients. If v4l2_ycbcr_encoding is extended with new
3058c2ecf20Sopenharmony_ci	 * values, the mapping below should be updated.
3068c2ecf20Sopenharmony_ci	 *
3078c2ecf20Sopenharmony_ci	 * Substitutions are taken from the mapping given for
3088c2ecf20Sopenharmony_ci	 * V4L2_YCBCR_ENC_DEFAULT documented in videodev2.h.
3098c2ecf20Sopenharmony_ci	 *
3108c2ecf20Sopenharmony_ci	 * FCC is assumed to be close enough to 601.
3118c2ecf20Sopenharmony_ci	 */
3128c2ecf20Sopenharmony_ci	static const enum v4l2_ycbcr_encoding ycbcr_encs[] = {
3138c2ecf20Sopenharmony_ci		V4L2_YCBCR_ENC_DEFAULT,  /* Unspecified */
3148c2ecf20Sopenharmony_ci		V4L2_YCBCR_ENC_709,
3158c2ecf20Sopenharmony_ci		V4L2_YCBCR_ENC_601,      /* Substitution for FCC */
3168c2ecf20Sopenharmony_ci		V4L2_YCBCR_ENC_601,      /* Substitution for BT.470-2 B, G */
3178c2ecf20Sopenharmony_ci		V4L2_YCBCR_ENC_601,
3188c2ecf20Sopenharmony_ci		V4L2_YCBCR_ENC_SMPTE240M,
3198c2ecf20Sopenharmony_ci	};
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (matrix_coefficients < ARRAY_SIZE(ycbcr_encs))
3228c2ecf20Sopenharmony_ci		return ycbcr_encs[matrix_coefficients];
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	return V4L2_YCBCR_ENC_DEFAULT;  /* Reserved */
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci/* Simplify a fraction using a simple continued fraction decomposition. The
3288c2ecf20Sopenharmony_ci * idea here is to convert fractions such as 333333/10000000 to 1/30 using
3298c2ecf20Sopenharmony_ci * 32 bit arithmetic only. The algorithm is not perfect and relies upon two
3308c2ecf20Sopenharmony_ci * arbitrary parameters to remove non-significative terms from the simple
3318c2ecf20Sopenharmony_ci * continued fraction decomposition. Using 8 and 333 for n_terms and threshold
3328c2ecf20Sopenharmony_ci * respectively seems to give nice results.
3338c2ecf20Sopenharmony_ci */
3348c2ecf20Sopenharmony_civoid uvc_simplify_fraction(u32 *numerator, u32 *denominator,
3358c2ecf20Sopenharmony_ci		unsigned int n_terms, unsigned int threshold)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	u32 *an;
3388c2ecf20Sopenharmony_ci	u32 x, y, r;
3398c2ecf20Sopenharmony_ci	unsigned int i, n;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	an = kmalloc_array(n_terms, sizeof(*an), GFP_KERNEL);
3428c2ecf20Sopenharmony_ci	if (an == NULL)
3438c2ecf20Sopenharmony_ci		return;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	/* Convert the fraction to a simple continued fraction. See
3468c2ecf20Sopenharmony_ci	 * https://mathforum.org/dr.math/faq/faq.fractions.html
3478c2ecf20Sopenharmony_ci	 * Stop if the current term is bigger than or equal to the given
3488c2ecf20Sopenharmony_ci	 * threshold.
3498c2ecf20Sopenharmony_ci	 */
3508c2ecf20Sopenharmony_ci	x = *numerator;
3518c2ecf20Sopenharmony_ci	y = *denominator;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	for (n = 0; n < n_terms && y != 0; ++n) {
3548c2ecf20Sopenharmony_ci		an[n] = x / y;
3558c2ecf20Sopenharmony_ci		if (an[n] >= threshold) {
3568c2ecf20Sopenharmony_ci			if (n < 2)
3578c2ecf20Sopenharmony_ci				n++;
3588c2ecf20Sopenharmony_ci			break;
3598c2ecf20Sopenharmony_ci		}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci		r = x - an[n] * y;
3628c2ecf20Sopenharmony_ci		x = y;
3638c2ecf20Sopenharmony_ci		y = r;
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	/* Expand the simple continued fraction back to an integer fraction. */
3678c2ecf20Sopenharmony_ci	x = 0;
3688c2ecf20Sopenharmony_ci	y = 1;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	for (i = n; i > 0; --i) {
3718c2ecf20Sopenharmony_ci		r = y;
3728c2ecf20Sopenharmony_ci		y = an[i-1] * y + x;
3738c2ecf20Sopenharmony_ci		x = r;
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	*numerator = y;
3778c2ecf20Sopenharmony_ci	*denominator = x;
3788c2ecf20Sopenharmony_ci	kfree(an);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci/* Convert a fraction to a frame interval in 100ns multiples. The idea here is
3828c2ecf20Sopenharmony_ci * to compute numerator / denominator * 10000000 using 32 bit fixed point
3838c2ecf20Sopenharmony_ci * arithmetic only.
3848c2ecf20Sopenharmony_ci */
3858c2ecf20Sopenharmony_ciu32 uvc_fraction_to_interval(u32 numerator, u32 denominator)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	u32 multiplier;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/* Saturate the result if the operation would overflow. */
3908c2ecf20Sopenharmony_ci	if (denominator == 0 ||
3918c2ecf20Sopenharmony_ci	    numerator/denominator >= ((u32)-1)/10000000)
3928c2ecf20Sopenharmony_ci		return (u32)-1;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	/* Divide both the denominator and the multiplier by two until
3958c2ecf20Sopenharmony_ci	 * numerator * multiplier doesn't overflow. If anyone knows a better
3968c2ecf20Sopenharmony_ci	 * algorithm please let me know.
3978c2ecf20Sopenharmony_ci	 */
3988c2ecf20Sopenharmony_ci	multiplier = 10000000;
3998c2ecf20Sopenharmony_ci	while (numerator > ((u32)-1)/multiplier) {
4008c2ecf20Sopenharmony_ci		multiplier /= 2;
4018c2ecf20Sopenharmony_ci		denominator /= 2;
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	return denominator ? numerator * multiplier / denominator : 0;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------
4088c2ecf20Sopenharmony_ci * Terminal and unit management
4098c2ecf20Sopenharmony_ci */
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistruct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct uvc_entity *entity;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	list_for_each_entry(entity, &dev->entities, list) {
4168c2ecf20Sopenharmony_ci		if (entity->id == id)
4178c2ecf20Sopenharmony_ci			return entity;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return NULL;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
4248c2ecf20Sopenharmony_ci	int id, struct uvc_entity *entity)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	unsigned int i;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (entity == NULL)
4298c2ecf20Sopenharmony_ci		entity = list_entry(&dev->entities, struct uvc_entity, list);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	list_for_each_entry_continue(entity, &dev->entities, list) {
4328c2ecf20Sopenharmony_ci		for (i = 0; i < entity->bNrInPins; ++i)
4338c2ecf20Sopenharmony_ci			if (entity->baSourceID[i] == id)
4348c2ecf20Sopenharmony_ci				return entity;
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	return NULL;
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	struct uvc_streaming *stream;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	list_for_each_entry(stream, &dev->streams, list) {
4458c2ecf20Sopenharmony_ci		if (stream->header.bTerminalLink == id)
4468c2ecf20Sopenharmony_ci			return stream;
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	return NULL;
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------
4538c2ecf20Sopenharmony_ci * Streaming Object Management
4548c2ecf20Sopenharmony_ci */
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic void uvc_stream_delete(struct uvc_streaming *stream)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	if (stream->async_wq)
4598c2ecf20Sopenharmony_ci		destroy_workqueue(stream->async_wq);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	mutex_destroy(&stream->mutex);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	usb_put_intf(stream->intf);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	kfree(stream->format);
4668c2ecf20Sopenharmony_ci	kfree(stream->header.bmaControls);
4678c2ecf20Sopenharmony_ci	kfree(stream);
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cistatic struct uvc_streaming *uvc_stream_new(struct uvc_device *dev,
4718c2ecf20Sopenharmony_ci					    struct usb_interface *intf)
4728c2ecf20Sopenharmony_ci{
4738c2ecf20Sopenharmony_ci	struct uvc_streaming *stream;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
4768c2ecf20Sopenharmony_ci	if (stream == NULL)
4778c2ecf20Sopenharmony_ci		return NULL;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	mutex_init(&stream->mutex);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	stream->dev = dev;
4828c2ecf20Sopenharmony_ci	stream->intf = usb_get_intf(intf);
4838c2ecf20Sopenharmony_ci	stream->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	/* Allocate a stream specific work queue for asynchronous tasks. */
4868c2ecf20Sopenharmony_ci	stream->async_wq = alloc_workqueue("uvcvideo", WQ_UNBOUND | WQ_HIGHPRI,
4878c2ecf20Sopenharmony_ci					   0);
4888c2ecf20Sopenharmony_ci	if (!stream->async_wq) {
4898c2ecf20Sopenharmony_ci		uvc_stream_delete(stream);
4908c2ecf20Sopenharmony_ci		return NULL;
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	return stream;
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------
4978c2ecf20Sopenharmony_ci * Descriptors parsing
4988c2ecf20Sopenharmony_ci */
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic int uvc_parse_format(struct uvc_device *dev,
5018c2ecf20Sopenharmony_ci	struct uvc_streaming *streaming, struct uvc_format *format,
5028c2ecf20Sopenharmony_ci	u32 **intervals, unsigned char *buffer, int buflen)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	struct usb_interface *intf = streaming->intf;
5058c2ecf20Sopenharmony_ci	struct usb_host_interface *alts = intf->cur_altsetting;
5068c2ecf20Sopenharmony_ci	struct uvc_format_desc *fmtdesc;
5078c2ecf20Sopenharmony_ci	struct uvc_frame *frame;
5088c2ecf20Sopenharmony_ci	const unsigned char *start = buffer;
5098c2ecf20Sopenharmony_ci	unsigned int width_multiplier = 1;
5108c2ecf20Sopenharmony_ci	unsigned int interval;
5118c2ecf20Sopenharmony_ci	unsigned int i, n;
5128c2ecf20Sopenharmony_ci	u8 ftype;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	format->type = buffer[2];
5158c2ecf20Sopenharmony_ci	format->index = buffer[3];
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	switch (buffer[2]) {
5188c2ecf20Sopenharmony_ci	case UVC_VS_FORMAT_UNCOMPRESSED:
5198c2ecf20Sopenharmony_ci	case UVC_VS_FORMAT_FRAME_BASED:
5208c2ecf20Sopenharmony_ci		n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28;
5218c2ecf20Sopenharmony_ci		if (buflen < n) {
5228c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
5238c2ecf20Sopenharmony_ci			       "interface %d FORMAT error\n",
5248c2ecf20Sopenharmony_ci			       dev->udev->devnum,
5258c2ecf20Sopenharmony_ci			       alts->desc.bInterfaceNumber);
5268c2ecf20Sopenharmony_ci			return -EINVAL;
5278c2ecf20Sopenharmony_ci		}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci		/* Find the format descriptor from its GUID. */
5308c2ecf20Sopenharmony_ci		fmtdesc = uvc_format_by_guid(&buffer[5]);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci		if (fmtdesc != NULL) {
5338c2ecf20Sopenharmony_ci			strscpy(format->name, fmtdesc->name,
5348c2ecf20Sopenharmony_ci				sizeof(format->name));
5358c2ecf20Sopenharmony_ci			format->fcc = fmtdesc->fcc;
5368c2ecf20Sopenharmony_ci		} else {
5378c2ecf20Sopenharmony_ci			uvc_printk(KERN_INFO, "Unknown video format %pUl\n",
5388c2ecf20Sopenharmony_ci				&buffer[5]);
5398c2ecf20Sopenharmony_ci			snprintf(format->name, sizeof(format->name), "%pUl\n",
5408c2ecf20Sopenharmony_ci				&buffer[5]);
5418c2ecf20Sopenharmony_ci			format->fcc = 0;
5428c2ecf20Sopenharmony_ci		}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci		format->bpp = buffer[21];
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci		/* Some devices report a format that doesn't match what they
5478c2ecf20Sopenharmony_ci		 * really send.
5488c2ecf20Sopenharmony_ci		 */
5498c2ecf20Sopenharmony_ci		if (dev->quirks & UVC_QUIRK_FORCE_Y8) {
5508c2ecf20Sopenharmony_ci			if (format->fcc == V4L2_PIX_FMT_YUYV) {
5518c2ecf20Sopenharmony_ci				strscpy(format->name, "Greyscale 8-bit (Y8  )",
5528c2ecf20Sopenharmony_ci					sizeof(format->name));
5538c2ecf20Sopenharmony_ci				format->fcc = V4L2_PIX_FMT_GREY;
5548c2ecf20Sopenharmony_ci				format->bpp = 8;
5558c2ecf20Sopenharmony_ci				width_multiplier = 2;
5568c2ecf20Sopenharmony_ci			}
5578c2ecf20Sopenharmony_ci		}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci		/* Some devices report bpp that doesn't match the format. */
5608c2ecf20Sopenharmony_ci		if (dev->quirks & UVC_QUIRK_FORCE_BPP) {
5618c2ecf20Sopenharmony_ci			const struct v4l2_format_info *info =
5628c2ecf20Sopenharmony_ci				v4l2_format_info(format->fcc);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci			if (info) {
5658c2ecf20Sopenharmony_ci				unsigned int div = info->hdiv * info->vdiv;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci				n = info->bpp[0] * div;
5688c2ecf20Sopenharmony_ci				for (i = 1; i < info->comp_planes; i++)
5698c2ecf20Sopenharmony_ci					n += info->bpp[i];
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci				format->bpp = DIV_ROUND_UP(8 * n, div);
5728c2ecf20Sopenharmony_ci			}
5738c2ecf20Sopenharmony_ci		}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci		if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) {
5768c2ecf20Sopenharmony_ci			ftype = UVC_VS_FRAME_UNCOMPRESSED;
5778c2ecf20Sopenharmony_ci		} else {
5788c2ecf20Sopenharmony_ci			ftype = UVC_VS_FRAME_FRAME_BASED;
5798c2ecf20Sopenharmony_ci			if (buffer[27])
5808c2ecf20Sopenharmony_ci				format->flags = UVC_FMT_FLAG_COMPRESSED;
5818c2ecf20Sopenharmony_ci		}
5828c2ecf20Sopenharmony_ci		break;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	case UVC_VS_FORMAT_MJPEG:
5858c2ecf20Sopenharmony_ci		if (buflen < 11) {
5868c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
5878c2ecf20Sopenharmony_ci			       "interface %d FORMAT error\n",
5888c2ecf20Sopenharmony_ci			       dev->udev->devnum,
5898c2ecf20Sopenharmony_ci			       alts->desc.bInterfaceNumber);
5908c2ecf20Sopenharmony_ci			return -EINVAL;
5918c2ecf20Sopenharmony_ci		}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci		strscpy(format->name, "MJPEG", sizeof(format->name));
5948c2ecf20Sopenharmony_ci		format->fcc = V4L2_PIX_FMT_MJPEG;
5958c2ecf20Sopenharmony_ci		format->flags = UVC_FMT_FLAG_COMPRESSED;
5968c2ecf20Sopenharmony_ci		format->bpp = 0;
5978c2ecf20Sopenharmony_ci		ftype = UVC_VS_FRAME_MJPEG;
5988c2ecf20Sopenharmony_ci		break;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	case UVC_VS_FORMAT_DV:
6018c2ecf20Sopenharmony_ci		if (buflen < 9) {
6028c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
6038c2ecf20Sopenharmony_ci			       "interface %d FORMAT error\n",
6048c2ecf20Sopenharmony_ci			       dev->udev->devnum,
6058c2ecf20Sopenharmony_ci			       alts->desc.bInterfaceNumber);
6068c2ecf20Sopenharmony_ci			return -EINVAL;
6078c2ecf20Sopenharmony_ci		}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci		switch (buffer[8] & 0x7f) {
6108c2ecf20Sopenharmony_ci		case 0:
6118c2ecf20Sopenharmony_ci			strscpy(format->name, "SD-DV", sizeof(format->name));
6128c2ecf20Sopenharmony_ci			break;
6138c2ecf20Sopenharmony_ci		case 1:
6148c2ecf20Sopenharmony_ci			strscpy(format->name, "SDL-DV", sizeof(format->name));
6158c2ecf20Sopenharmony_ci			break;
6168c2ecf20Sopenharmony_ci		case 2:
6178c2ecf20Sopenharmony_ci			strscpy(format->name, "HD-DV", sizeof(format->name));
6188c2ecf20Sopenharmony_ci			break;
6198c2ecf20Sopenharmony_ci		default:
6208c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
6218c2ecf20Sopenharmony_ci			       "interface %d: unknown DV format %u\n",
6228c2ecf20Sopenharmony_ci			       dev->udev->devnum,
6238c2ecf20Sopenharmony_ci			       alts->desc.bInterfaceNumber, buffer[8]);
6248c2ecf20Sopenharmony_ci			return -EINVAL;
6258c2ecf20Sopenharmony_ci		}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci		strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz",
6288c2ecf20Sopenharmony_ci			sizeof(format->name));
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci		format->fcc = V4L2_PIX_FMT_DV;
6318c2ecf20Sopenharmony_ci		format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM;
6328c2ecf20Sopenharmony_ci		format->bpp = 0;
6338c2ecf20Sopenharmony_ci		ftype = 0;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci		/* Create a dummy frame descriptor. */
6368c2ecf20Sopenharmony_ci		frame = &format->frame[0];
6378c2ecf20Sopenharmony_ci		memset(&format->frame[0], 0, sizeof(format->frame[0]));
6388c2ecf20Sopenharmony_ci		frame->bFrameIntervalType = 1;
6398c2ecf20Sopenharmony_ci		frame->dwDefaultFrameInterval = 1;
6408c2ecf20Sopenharmony_ci		frame->dwFrameInterval = *intervals;
6418c2ecf20Sopenharmony_ci		*(*intervals)++ = 1;
6428c2ecf20Sopenharmony_ci		format->nframes = 1;
6438c2ecf20Sopenharmony_ci		break;
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	case UVC_VS_FORMAT_MPEG2TS:
6468c2ecf20Sopenharmony_ci	case UVC_VS_FORMAT_STREAM_BASED:
6478c2ecf20Sopenharmony_ci		/* Not supported yet. */
6488c2ecf20Sopenharmony_ci	default:
6498c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
6508c2ecf20Sopenharmony_ci		       "interface %d unsupported format %u\n",
6518c2ecf20Sopenharmony_ci		       dev->udev->devnum, alts->desc.bInterfaceNumber,
6528c2ecf20Sopenharmony_ci		       buffer[2]);
6538c2ecf20Sopenharmony_ci		return -EINVAL;
6548c2ecf20Sopenharmony_ci	}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	buflen -= buffer[0];
6598c2ecf20Sopenharmony_ci	buffer += buffer[0];
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	/* Parse the frame descriptors. Only uncompressed, MJPEG and frame
6628c2ecf20Sopenharmony_ci	 * based formats have frame descriptors.
6638c2ecf20Sopenharmony_ci	 */
6648c2ecf20Sopenharmony_ci	while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
6658c2ecf20Sopenharmony_ci	       buffer[2] == ftype) {
6668c2ecf20Sopenharmony_ci		frame = &format->frame[format->nframes];
6678c2ecf20Sopenharmony_ci		if (ftype != UVC_VS_FRAME_FRAME_BASED)
6688c2ecf20Sopenharmony_ci			n = buflen > 25 ? buffer[25] : 0;
6698c2ecf20Sopenharmony_ci		else
6708c2ecf20Sopenharmony_ci			n = buflen > 21 ? buffer[21] : 0;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci		n = n ? n : 3;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci		if (buflen < 26 + 4*n) {
6758c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
6768c2ecf20Sopenharmony_ci			       "interface %d FRAME error\n", dev->udev->devnum,
6778c2ecf20Sopenharmony_ci			       alts->desc.bInterfaceNumber);
6788c2ecf20Sopenharmony_ci			return -EINVAL;
6798c2ecf20Sopenharmony_ci		}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci		frame->bFrameIndex = buffer[3];
6828c2ecf20Sopenharmony_ci		frame->bmCapabilities = buffer[4];
6838c2ecf20Sopenharmony_ci		frame->wWidth = get_unaligned_le16(&buffer[5])
6848c2ecf20Sopenharmony_ci			      * width_multiplier;
6858c2ecf20Sopenharmony_ci		frame->wHeight = get_unaligned_le16(&buffer[7]);
6868c2ecf20Sopenharmony_ci		frame->dwMinBitRate = get_unaligned_le32(&buffer[9]);
6878c2ecf20Sopenharmony_ci		frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]);
6888c2ecf20Sopenharmony_ci		if (ftype != UVC_VS_FRAME_FRAME_BASED) {
6898c2ecf20Sopenharmony_ci			frame->dwMaxVideoFrameBufferSize =
6908c2ecf20Sopenharmony_ci				get_unaligned_le32(&buffer[17]);
6918c2ecf20Sopenharmony_ci			frame->dwDefaultFrameInterval =
6928c2ecf20Sopenharmony_ci				get_unaligned_le32(&buffer[21]);
6938c2ecf20Sopenharmony_ci			frame->bFrameIntervalType = buffer[25];
6948c2ecf20Sopenharmony_ci		} else {
6958c2ecf20Sopenharmony_ci			frame->dwMaxVideoFrameBufferSize = 0;
6968c2ecf20Sopenharmony_ci			frame->dwDefaultFrameInterval =
6978c2ecf20Sopenharmony_ci				get_unaligned_le32(&buffer[17]);
6988c2ecf20Sopenharmony_ci			frame->bFrameIntervalType = buffer[21];
6998c2ecf20Sopenharmony_ci		}
7008c2ecf20Sopenharmony_ci		frame->dwFrameInterval = *intervals;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci		/* Several UVC chipsets screw up dwMaxVideoFrameBufferSize
7038c2ecf20Sopenharmony_ci		 * completely. Observed behaviours range from setting the
7048c2ecf20Sopenharmony_ci		 * value to 1.1x the actual frame size to hardwiring the
7058c2ecf20Sopenharmony_ci		 * 16 low bits to 0. This results in a higher than necessary
7068c2ecf20Sopenharmony_ci		 * memory usage as well as a wrong image size information. For
7078c2ecf20Sopenharmony_ci		 * uncompressed formats this can be fixed by computing the
7088c2ecf20Sopenharmony_ci		 * value from the frame size.
7098c2ecf20Sopenharmony_ci		 */
7108c2ecf20Sopenharmony_ci		if (!(format->flags & UVC_FMT_FLAG_COMPRESSED))
7118c2ecf20Sopenharmony_ci			frame->dwMaxVideoFrameBufferSize = format->bpp
7128c2ecf20Sopenharmony_ci				* frame->wWidth * frame->wHeight / 8;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci		/* Some bogus devices report dwMinFrameInterval equal to
7158c2ecf20Sopenharmony_ci		 * dwMaxFrameInterval and have dwFrameIntervalStep set to
7168c2ecf20Sopenharmony_ci		 * zero. Setting all null intervals to 1 fixes the problem and
7178c2ecf20Sopenharmony_ci		 * some other divisions by zero that could happen.
7188c2ecf20Sopenharmony_ci		 */
7198c2ecf20Sopenharmony_ci		for (i = 0; i < n; ++i) {
7208c2ecf20Sopenharmony_ci			interval = get_unaligned_le32(&buffer[26+4*i]);
7218c2ecf20Sopenharmony_ci			*(*intervals)++ = interval ? interval : 1;
7228c2ecf20Sopenharmony_ci		}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci		/* Make sure that the default frame interval stays between
7258c2ecf20Sopenharmony_ci		 * the boundaries.
7268c2ecf20Sopenharmony_ci		 */
7278c2ecf20Sopenharmony_ci		n -= frame->bFrameIntervalType ? 1 : 2;
7288c2ecf20Sopenharmony_ci		frame->dwDefaultFrameInterval =
7298c2ecf20Sopenharmony_ci			min(frame->dwFrameInterval[n],
7308c2ecf20Sopenharmony_ci			    max(frame->dwFrameInterval[0],
7318c2ecf20Sopenharmony_ci				frame->dwDefaultFrameInterval));
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci		if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) {
7348c2ecf20Sopenharmony_ci			frame->bFrameIntervalType = 1;
7358c2ecf20Sopenharmony_ci			frame->dwFrameInterval[0] =
7368c2ecf20Sopenharmony_ci				frame->dwDefaultFrameInterval;
7378c2ecf20Sopenharmony_ci		}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n",
7408c2ecf20Sopenharmony_ci			frame->wWidth, frame->wHeight,
7418c2ecf20Sopenharmony_ci			10000000/frame->dwDefaultFrameInterval,
7428c2ecf20Sopenharmony_ci			(100000000/frame->dwDefaultFrameInterval)%10);
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci		format->nframes++;
7458c2ecf20Sopenharmony_ci		buflen -= buffer[0];
7468c2ecf20Sopenharmony_ci		buffer += buffer[0];
7478c2ecf20Sopenharmony_ci	}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
7508c2ecf20Sopenharmony_ci	    buffer[2] == UVC_VS_STILL_IMAGE_FRAME) {
7518c2ecf20Sopenharmony_ci		buflen -= buffer[0];
7528c2ecf20Sopenharmony_ci		buffer += buffer[0];
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE &&
7568c2ecf20Sopenharmony_ci	    buffer[2] == UVC_VS_COLORFORMAT) {
7578c2ecf20Sopenharmony_ci		if (buflen < 6) {
7588c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
7598c2ecf20Sopenharmony_ci			       "interface %d COLORFORMAT error\n",
7608c2ecf20Sopenharmony_ci			       dev->udev->devnum,
7618c2ecf20Sopenharmony_ci			       alts->desc.bInterfaceNumber);
7628c2ecf20Sopenharmony_ci			return -EINVAL;
7638c2ecf20Sopenharmony_ci		}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci		format->colorspace = uvc_colorspace(buffer[3]);
7668c2ecf20Sopenharmony_ci		format->xfer_func = uvc_xfer_func(buffer[4]);
7678c2ecf20Sopenharmony_ci		format->ycbcr_enc = uvc_ycbcr_enc(buffer[5]);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci		buflen -= buffer[0];
7708c2ecf20Sopenharmony_ci		buffer += buffer[0];
7718c2ecf20Sopenharmony_ci	}
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	return buffer - start;
7748c2ecf20Sopenharmony_ci}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_cistatic int uvc_parse_streaming(struct uvc_device *dev,
7778c2ecf20Sopenharmony_ci	struct usb_interface *intf)
7788c2ecf20Sopenharmony_ci{
7798c2ecf20Sopenharmony_ci	struct uvc_streaming *streaming = NULL;
7808c2ecf20Sopenharmony_ci	struct uvc_format *format;
7818c2ecf20Sopenharmony_ci	struct uvc_frame *frame;
7828c2ecf20Sopenharmony_ci	struct usb_host_interface *alts = &intf->altsetting[0];
7838c2ecf20Sopenharmony_ci	unsigned char *_buffer, *buffer = alts->extra;
7848c2ecf20Sopenharmony_ci	int _buflen, buflen = alts->extralen;
7858c2ecf20Sopenharmony_ci	unsigned int nformats = 0, nframes = 0, nintervals = 0;
7868c2ecf20Sopenharmony_ci	unsigned int size, i, n, p;
7878c2ecf20Sopenharmony_ci	u32 *interval;
7888c2ecf20Sopenharmony_ci	u16 psize;
7898c2ecf20Sopenharmony_ci	int ret = -EINVAL;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	if (intf->cur_altsetting->desc.bInterfaceSubClass
7928c2ecf20Sopenharmony_ci		!= UVC_SC_VIDEOSTREAMING) {
7938c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a "
7948c2ecf20Sopenharmony_ci			"video streaming interface\n", dev->udev->devnum,
7958c2ecf20Sopenharmony_ci			intf->altsetting[0].desc.bInterfaceNumber);
7968c2ecf20Sopenharmony_ci		return -EINVAL;
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) {
8008c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already "
8018c2ecf20Sopenharmony_ci			"claimed\n", dev->udev->devnum,
8028c2ecf20Sopenharmony_ci			intf->altsetting[0].desc.bInterfaceNumber);
8038c2ecf20Sopenharmony_ci		return -EINVAL;
8048c2ecf20Sopenharmony_ci	}
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	streaming = uvc_stream_new(dev, intf);
8078c2ecf20Sopenharmony_ci	if (streaming == NULL) {
8088c2ecf20Sopenharmony_ci		usb_driver_release_interface(&uvc_driver.driver, intf);
8098c2ecf20Sopenharmony_ci		return -ENOMEM;
8108c2ecf20Sopenharmony_ci	}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	/* The Pico iMage webcam has its class-specific interface descriptors
8138c2ecf20Sopenharmony_ci	 * after the endpoint descriptors.
8148c2ecf20Sopenharmony_ci	 */
8158c2ecf20Sopenharmony_ci	if (buflen == 0) {
8168c2ecf20Sopenharmony_ci		for (i = 0; i < alts->desc.bNumEndpoints; ++i) {
8178c2ecf20Sopenharmony_ci			struct usb_host_endpoint *ep = &alts->endpoint[i];
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci			if (ep->extralen == 0)
8208c2ecf20Sopenharmony_ci				continue;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci			if (ep->extralen > 2 &&
8238c2ecf20Sopenharmony_ci			    ep->extra[1] == USB_DT_CS_INTERFACE) {
8248c2ecf20Sopenharmony_ci				uvc_trace(UVC_TRACE_DESCR, "trying extra data "
8258c2ecf20Sopenharmony_ci					"from endpoint %u.\n", i);
8268c2ecf20Sopenharmony_ci				buffer = alts->endpoint[i].extra;
8278c2ecf20Sopenharmony_ci				buflen = alts->endpoint[i].extralen;
8288c2ecf20Sopenharmony_ci				break;
8298c2ecf20Sopenharmony_ci			}
8308c2ecf20Sopenharmony_ci		}
8318c2ecf20Sopenharmony_ci	}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	/* Skip the standard interface descriptors. */
8348c2ecf20Sopenharmony_ci	while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) {
8358c2ecf20Sopenharmony_ci		buflen -= buffer[0];
8368c2ecf20Sopenharmony_ci		buffer += buffer[0];
8378c2ecf20Sopenharmony_ci	}
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	if (buflen <= 2) {
8408c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming "
8418c2ecf20Sopenharmony_ci			"interface descriptors found.\n");
8428c2ecf20Sopenharmony_ci		goto error;
8438c2ecf20Sopenharmony_ci	}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	/* Parse the header descriptor. */
8468c2ecf20Sopenharmony_ci	switch (buffer[2]) {
8478c2ecf20Sopenharmony_ci	case UVC_VS_OUTPUT_HEADER:
8488c2ecf20Sopenharmony_ci		streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
8498c2ecf20Sopenharmony_ci		size = 9;
8508c2ecf20Sopenharmony_ci		break;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	case UVC_VS_INPUT_HEADER:
8538c2ecf20Sopenharmony_ci		streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
8548c2ecf20Sopenharmony_ci		size = 13;
8558c2ecf20Sopenharmony_ci		break;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	default:
8588c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
8598c2ecf20Sopenharmony_ci			"%d HEADER descriptor not found.\n", dev->udev->devnum,
8608c2ecf20Sopenharmony_ci			alts->desc.bInterfaceNumber);
8618c2ecf20Sopenharmony_ci		goto error;
8628c2ecf20Sopenharmony_ci	}
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	p = buflen >= 4 ? buffer[3] : 0;
8658c2ecf20Sopenharmony_ci	n = buflen >= size ? buffer[size-1] : 0;
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	if (buflen < size + p*n) {
8688c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
8698c2ecf20Sopenharmony_ci			"interface %d HEADER descriptor is invalid.\n",
8708c2ecf20Sopenharmony_ci			dev->udev->devnum, alts->desc.bInterfaceNumber);
8718c2ecf20Sopenharmony_ci		goto error;
8728c2ecf20Sopenharmony_ci	}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	streaming->header.bNumFormats = p;
8758c2ecf20Sopenharmony_ci	streaming->header.bEndpointAddress = buffer[6];
8768c2ecf20Sopenharmony_ci	if (buffer[2] == UVC_VS_INPUT_HEADER) {
8778c2ecf20Sopenharmony_ci		streaming->header.bmInfo = buffer[7];
8788c2ecf20Sopenharmony_ci		streaming->header.bTerminalLink = buffer[8];
8798c2ecf20Sopenharmony_ci		streaming->header.bStillCaptureMethod = buffer[9];
8808c2ecf20Sopenharmony_ci		streaming->header.bTriggerSupport = buffer[10];
8818c2ecf20Sopenharmony_ci		streaming->header.bTriggerUsage = buffer[11];
8828c2ecf20Sopenharmony_ci	} else {
8838c2ecf20Sopenharmony_ci		streaming->header.bTerminalLink = buffer[7];
8848c2ecf20Sopenharmony_ci	}
8858c2ecf20Sopenharmony_ci	streaming->header.bControlSize = n;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	streaming->header.bmaControls = kmemdup(&buffer[size], p * n,
8888c2ecf20Sopenharmony_ci						GFP_KERNEL);
8898c2ecf20Sopenharmony_ci	if (streaming->header.bmaControls == NULL) {
8908c2ecf20Sopenharmony_ci		ret = -ENOMEM;
8918c2ecf20Sopenharmony_ci		goto error;
8928c2ecf20Sopenharmony_ci	}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	buflen -= buffer[0];
8958c2ecf20Sopenharmony_ci	buffer += buffer[0];
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	_buffer = buffer;
8988c2ecf20Sopenharmony_ci	_buflen = buflen;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	/* Count the format and frame descriptors. */
9018c2ecf20Sopenharmony_ci	while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) {
9028c2ecf20Sopenharmony_ci		switch (_buffer[2]) {
9038c2ecf20Sopenharmony_ci		case UVC_VS_FORMAT_UNCOMPRESSED:
9048c2ecf20Sopenharmony_ci		case UVC_VS_FORMAT_MJPEG:
9058c2ecf20Sopenharmony_ci		case UVC_VS_FORMAT_FRAME_BASED:
9068c2ecf20Sopenharmony_ci			nformats++;
9078c2ecf20Sopenharmony_ci			break;
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci		case UVC_VS_FORMAT_DV:
9108c2ecf20Sopenharmony_ci			/* DV format has no frame descriptor. We will create a
9118c2ecf20Sopenharmony_ci			 * dummy frame descriptor with a dummy frame interval.
9128c2ecf20Sopenharmony_ci			 */
9138c2ecf20Sopenharmony_ci			nformats++;
9148c2ecf20Sopenharmony_ci			nframes++;
9158c2ecf20Sopenharmony_ci			nintervals++;
9168c2ecf20Sopenharmony_ci			break;
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci		case UVC_VS_FORMAT_MPEG2TS:
9198c2ecf20Sopenharmony_ci		case UVC_VS_FORMAT_STREAM_BASED:
9208c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
9218c2ecf20Sopenharmony_ci				"interface %d FORMAT %u is not supported.\n",
9228c2ecf20Sopenharmony_ci				dev->udev->devnum,
9238c2ecf20Sopenharmony_ci				alts->desc.bInterfaceNumber, _buffer[2]);
9248c2ecf20Sopenharmony_ci			break;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci		case UVC_VS_FRAME_UNCOMPRESSED:
9278c2ecf20Sopenharmony_ci		case UVC_VS_FRAME_MJPEG:
9288c2ecf20Sopenharmony_ci			nframes++;
9298c2ecf20Sopenharmony_ci			if (_buflen > 25)
9308c2ecf20Sopenharmony_ci				nintervals += _buffer[25] ? _buffer[25] : 3;
9318c2ecf20Sopenharmony_ci			break;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci		case UVC_VS_FRAME_FRAME_BASED:
9348c2ecf20Sopenharmony_ci			nframes++;
9358c2ecf20Sopenharmony_ci			if (_buflen > 21)
9368c2ecf20Sopenharmony_ci				nintervals += _buffer[21] ? _buffer[21] : 3;
9378c2ecf20Sopenharmony_ci			break;
9388c2ecf20Sopenharmony_ci		}
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci		_buflen -= _buffer[0];
9418c2ecf20Sopenharmony_ci		_buffer += _buffer[0];
9428c2ecf20Sopenharmony_ci	}
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	if (nformats == 0) {
9458c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
9468c2ecf20Sopenharmony_ci			"%d has no supported formats defined.\n",
9478c2ecf20Sopenharmony_ci			dev->udev->devnum, alts->desc.bInterfaceNumber);
9488c2ecf20Sopenharmony_ci		goto error;
9498c2ecf20Sopenharmony_ci	}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	size = nformats * sizeof(*format) + nframes * sizeof(*frame)
9528c2ecf20Sopenharmony_ci	     + nintervals * sizeof(*interval);
9538c2ecf20Sopenharmony_ci	format = kzalloc(size, GFP_KERNEL);
9548c2ecf20Sopenharmony_ci	if (format == NULL) {
9558c2ecf20Sopenharmony_ci		ret = -ENOMEM;
9568c2ecf20Sopenharmony_ci		goto error;
9578c2ecf20Sopenharmony_ci	}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	frame = (struct uvc_frame *)&format[nformats];
9608c2ecf20Sopenharmony_ci	interval = (u32 *)&frame[nframes];
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	streaming->format = format;
9638c2ecf20Sopenharmony_ci	streaming->nformats = nformats;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	/* Parse the format descriptors. */
9668c2ecf20Sopenharmony_ci	while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) {
9678c2ecf20Sopenharmony_ci		switch (buffer[2]) {
9688c2ecf20Sopenharmony_ci		case UVC_VS_FORMAT_UNCOMPRESSED:
9698c2ecf20Sopenharmony_ci		case UVC_VS_FORMAT_MJPEG:
9708c2ecf20Sopenharmony_ci		case UVC_VS_FORMAT_DV:
9718c2ecf20Sopenharmony_ci		case UVC_VS_FORMAT_FRAME_BASED:
9728c2ecf20Sopenharmony_ci			format->frame = frame;
9738c2ecf20Sopenharmony_ci			ret = uvc_parse_format(dev, streaming, format,
9748c2ecf20Sopenharmony_ci				&interval, buffer, buflen);
9758c2ecf20Sopenharmony_ci			if (ret < 0)
9768c2ecf20Sopenharmony_ci				goto error;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci			frame += format->nframes;
9798c2ecf20Sopenharmony_ci			format++;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci			buflen -= ret;
9828c2ecf20Sopenharmony_ci			buffer += ret;
9838c2ecf20Sopenharmony_ci			continue;
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci		default:
9868c2ecf20Sopenharmony_ci			break;
9878c2ecf20Sopenharmony_ci		}
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci		buflen -= buffer[0];
9908c2ecf20Sopenharmony_ci		buffer += buffer[0];
9918c2ecf20Sopenharmony_ci	}
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	if (buflen)
9948c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
9958c2ecf20Sopenharmony_ci			"%d has %u bytes of trailing descriptor garbage.\n",
9968c2ecf20Sopenharmony_ci			dev->udev->devnum, alts->desc.bInterfaceNumber, buflen);
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	/* Parse the alternate settings to find the maximum bandwidth. */
9998c2ecf20Sopenharmony_ci	for (i = 0; i < intf->num_altsetting; ++i) {
10008c2ecf20Sopenharmony_ci		struct usb_host_endpoint *ep;
10018c2ecf20Sopenharmony_ci		alts = &intf->altsetting[i];
10028c2ecf20Sopenharmony_ci		ep = uvc_find_endpoint(alts,
10038c2ecf20Sopenharmony_ci				streaming->header.bEndpointAddress);
10048c2ecf20Sopenharmony_ci		if (ep == NULL)
10058c2ecf20Sopenharmony_ci			continue;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci		psize = le16_to_cpu(ep->desc.wMaxPacketSize);
10088c2ecf20Sopenharmony_ci		psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
10098c2ecf20Sopenharmony_ci		if (psize > streaming->maxpsize)
10108c2ecf20Sopenharmony_ci			streaming->maxpsize = psize;
10118c2ecf20Sopenharmony_ci	}
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	list_add_tail(&streaming->list, &dev->streams);
10148c2ecf20Sopenharmony_ci	return 0;
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_cierror:
10178c2ecf20Sopenharmony_ci	usb_driver_release_interface(&uvc_driver.driver, intf);
10188c2ecf20Sopenharmony_ci	uvc_stream_delete(streaming);
10198c2ecf20Sopenharmony_ci	return ret;
10208c2ecf20Sopenharmony_ci}
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_cistatic struct uvc_entity *uvc_alloc_entity(u16 type, u8 id,
10238c2ecf20Sopenharmony_ci		unsigned int num_pads, unsigned int extra_size)
10248c2ecf20Sopenharmony_ci{
10258c2ecf20Sopenharmony_ci	struct uvc_entity *entity;
10268c2ecf20Sopenharmony_ci	unsigned int num_inputs;
10278c2ecf20Sopenharmony_ci	unsigned int size;
10288c2ecf20Sopenharmony_ci	unsigned int i;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	extra_size = roundup(extra_size, sizeof(*entity->pads));
10318c2ecf20Sopenharmony_ci	if (num_pads)
10328c2ecf20Sopenharmony_ci		num_inputs = type & UVC_TERM_OUTPUT ? num_pads : num_pads - 1;
10338c2ecf20Sopenharmony_ci	else
10348c2ecf20Sopenharmony_ci		num_inputs = 0;
10358c2ecf20Sopenharmony_ci	size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads
10368c2ecf20Sopenharmony_ci	     + num_inputs;
10378c2ecf20Sopenharmony_ci	entity = kzalloc(size, GFP_KERNEL);
10388c2ecf20Sopenharmony_ci	if (entity == NULL)
10398c2ecf20Sopenharmony_ci		return NULL;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	entity->id = id;
10428c2ecf20Sopenharmony_ci	entity->type = type;
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	entity->num_links = 0;
10458c2ecf20Sopenharmony_ci	entity->num_pads = num_pads;
10468c2ecf20Sopenharmony_ci	entity->pads = ((void *)(entity + 1)) + extra_size;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	for (i = 0; i < num_inputs; ++i)
10498c2ecf20Sopenharmony_ci		entity->pads[i].flags = MEDIA_PAD_FL_SINK;
10508c2ecf20Sopenharmony_ci	if (!UVC_ENTITY_IS_OTERM(entity) && num_pads)
10518c2ecf20Sopenharmony_ci		entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE;
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	entity->bNrInPins = num_inputs;
10548c2ecf20Sopenharmony_ci	entity->baSourceID = (u8 *)(&entity->pads[num_pads]);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	return entity;
10578c2ecf20Sopenharmony_ci}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci/* Parse vendor-specific extensions. */
10608c2ecf20Sopenharmony_cistatic int uvc_parse_vendor_control(struct uvc_device *dev,
10618c2ecf20Sopenharmony_ci	const unsigned char *buffer, int buflen)
10628c2ecf20Sopenharmony_ci{
10638c2ecf20Sopenharmony_ci	struct usb_device *udev = dev->udev;
10648c2ecf20Sopenharmony_ci	struct usb_host_interface *alts = dev->intf->cur_altsetting;
10658c2ecf20Sopenharmony_ci	struct uvc_entity *unit;
10668c2ecf20Sopenharmony_ci	unsigned int n, p;
10678c2ecf20Sopenharmony_ci	int handled = 0;
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	switch (le16_to_cpu(dev->udev->descriptor.idVendor)) {
10708c2ecf20Sopenharmony_ci	case 0x046d:		/* Logitech */
10718c2ecf20Sopenharmony_ci		if (buffer[1] != 0x41 || buffer[2] != 0x01)
10728c2ecf20Sopenharmony_ci			break;
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci		/* Logitech implements several vendor specific functions
10758c2ecf20Sopenharmony_ci		 * through vendor specific extension units (LXU).
10768c2ecf20Sopenharmony_ci		 *
10778c2ecf20Sopenharmony_ci		 * The LXU descriptors are similar to XU descriptors
10788c2ecf20Sopenharmony_ci		 * (see "USB Device Video Class for Video Devices", section
10798c2ecf20Sopenharmony_ci		 * 3.7.2.6 "Extension Unit Descriptor") with the following
10808c2ecf20Sopenharmony_ci		 * differences:
10818c2ecf20Sopenharmony_ci		 *
10828c2ecf20Sopenharmony_ci		 * ----------------------------------------------------------
10838c2ecf20Sopenharmony_ci		 * 0		bLength		1	 Number
10848c2ecf20Sopenharmony_ci		 *	Size of this descriptor, in bytes: 24+p+n*2
10858c2ecf20Sopenharmony_ci		 * ----------------------------------------------------------
10868c2ecf20Sopenharmony_ci		 * 23+p+n	bmControlsType	N	Bitmap
10878c2ecf20Sopenharmony_ci		 *	Individual bits in the set are defined:
10888c2ecf20Sopenharmony_ci		 *	0: Absolute
10898c2ecf20Sopenharmony_ci		 *	1: Relative
10908c2ecf20Sopenharmony_ci		 *
10918c2ecf20Sopenharmony_ci		 *	This bitset is mapped exactly the same as bmControls.
10928c2ecf20Sopenharmony_ci		 * ----------------------------------------------------------
10938c2ecf20Sopenharmony_ci		 * 23+p+n*2	bReserved	1	Boolean
10948c2ecf20Sopenharmony_ci		 * ----------------------------------------------------------
10958c2ecf20Sopenharmony_ci		 * 24+p+n*2	iExtension	1	Index
10968c2ecf20Sopenharmony_ci		 *	Index of a string descriptor that describes this
10978c2ecf20Sopenharmony_ci		 *	extension unit.
10988c2ecf20Sopenharmony_ci		 * ----------------------------------------------------------
10998c2ecf20Sopenharmony_ci		 */
11008c2ecf20Sopenharmony_ci		p = buflen >= 22 ? buffer[21] : 0;
11018c2ecf20Sopenharmony_ci		n = buflen >= 25 + p ? buffer[22+p] : 0;
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci		if (buflen < 25 + p + 2*n) {
11048c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
11058c2ecf20Sopenharmony_ci				"interface %d EXTENSION_UNIT error\n",
11068c2ecf20Sopenharmony_ci				udev->devnum, alts->desc.bInterfaceNumber);
11078c2ecf20Sopenharmony_ci			break;
11088c2ecf20Sopenharmony_ci		}
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci		unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3],
11118c2ecf20Sopenharmony_ci					p + 1, 2*n);
11128c2ecf20Sopenharmony_ci		if (unit == NULL)
11138c2ecf20Sopenharmony_ci			return -ENOMEM;
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci		memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
11168c2ecf20Sopenharmony_ci		unit->extension.bNumControls = buffer[20];
11178c2ecf20Sopenharmony_ci		memcpy(unit->baSourceID, &buffer[22], p);
11188c2ecf20Sopenharmony_ci		unit->extension.bControlSize = buffer[22+p];
11198c2ecf20Sopenharmony_ci		unit->extension.bmControls = (u8 *)unit + sizeof(*unit);
11208c2ecf20Sopenharmony_ci		unit->extension.bmControlsType = (u8 *)unit + sizeof(*unit)
11218c2ecf20Sopenharmony_ci					       + n;
11228c2ecf20Sopenharmony_ci		memcpy(unit->extension.bmControls, &buffer[23+p], 2*n);
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci		if (buffer[24+p+2*n] == 0 ||
11258c2ecf20Sopenharmony_ci		    usb_string(udev, buffer[24+p+2*n], unit->name, sizeof(unit->name)) < 0)
11268c2ecf20Sopenharmony_ci			sprintf(unit->name, "Extension %u", buffer[3]);
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci		list_add_tail(&unit->list, &dev->entities);
11298c2ecf20Sopenharmony_ci		handled = 1;
11308c2ecf20Sopenharmony_ci		break;
11318c2ecf20Sopenharmony_ci	}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	return handled;
11348c2ecf20Sopenharmony_ci}
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_cistatic int uvc_parse_standard_control(struct uvc_device *dev,
11378c2ecf20Sopenharmony_ci	const unsigned char *buffer, int buflen)
11388c2ecf20Sopenharmony_ci{
11398c2ecf20Sopenharmony_ci	struct usb_device *udev = dev->udev;
11408c2ecf20Sopenharmony_ci	struct uvc_entity *unit, *term;
11418c2ecf20Sopenharmony_ci	struct usb_interface *intf;
11428c2ecf20Sopenharmony_ci	struct usb_host_interface *alts = dev->intf->cur_altsetting;
11438c2ecf20Sopenharmony_ci	unsigned int i, n, p, len;
11448c2ecf20Sopenharmony_ci	u16 type;
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	switch (buffer[2]) {
11478c2ecf20Sopenharmony_ci	case UVC_VC_HEADER:
11488c2ecf20Sopenharmony_ci		n = buflen >= 12 ? buffer[11] : 0;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci		if (buflen < 12 + n) {
11518c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
11528c2ecf20Sopenharmony_ci				"interface %d HEADER error\n", udev->devnum,
11538c2ecf20Sopenharmony_ci				alts->desc.bInterfaceNumber);
11548c2ecf20Sopenharmony_ci			return -EINVAL;
11558c2ecf20Sopenharmony_ci		}
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci		dev->uvc_version = get_unaligned_le16(&buffer[3]);
11588c2ecf20Sopenharmony_ci		dev->clock_frequency = get_unaligned_le32(&buffer[7]);
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci		/* Parse all USB Video Streaming interfaces. */
11618c2ecf20Sopenharmony_ci		for (i = 0; i < n; ++i) {
11628c2ecf20Sopenharmony_ci			intf = usb_ifnum_to_if(udev, buffer[12+i]);
11638c2ecf20Sopenharmony_ci			if (intf == NULL) {
11648c2ecf20Sopenharmony_ci				uvc_trace(UVC_TRACE_DESCR, "device %d "
11658c2ecf20Sopenharmony_ci					"interface %d doesn't exists\n",
11668c2ecf20Sopenharmony_ci					udev->devnum, i);
11678c2ecf20Sopenharmony_ci				continue;
11688c2ecf20Sopenharmony_ci			}
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci			uvc_parse_streaming(dev, intf);
11718c2ecf20Sopenharmony_ci		}
11728c2ecf20Sopenharmony_ci		break;
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	case UVC_VC_INPUT_TERMINAL:
11758c2ecf20Sopenharmony_ci		if (buflen < 8) {
11768c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
11778c2ecf20Sopenharmony_ci				"interface %d INPUT_TERMINAL error\n",
11788c2ecf20Sopenharmony_ci				udev->devnum, alts->desc.bInterfaceNumber);
11798c2ecf20Sopenharmony_ci			return -EINVAL;
11808c2ecf20Sopenharmony_ci		}
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci		/*
11838c2ecf20Sopenharmony_ci		 * Reject invalid terminal types that would cause issues:
11848c2ecf20Sopenharmony_ci		 *
11858c2ecf20Sopenharmony_ci		 * - The high byte must be non-zero, otherwise it would be
11868c2ecf20Sopenharmony_ci		 *   confused with a unit.
11878c2ecf20Sopenharmony_ci		 *
11888c2ecf20Sopenharmony_ci		 * - Bit 15 must be 0, as we use it internally as a terminal
11898c2ecf20Sopenharmony_ci		 *   direction flag.
11908c2ecf20Sopenharmony_ci		 *
11918c2ecf20Sopenharmony_ci		 * Other unknown types are accepted.
11928c2ecf20Sopenharmony_ci		 */
11938c2ecf20Sopenharmony_ci		type = get_unaligned_le16(&buffer[4]);
11948c2ecf20Sopenharmony_ci		if ((type & 0x7f00) == 0 || (type & 0x8000) != 0) {
11958c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
11968c2ecf20Sopenharmony_ci				"interface %d INPUT_TERMINAL %d has invalid "
11978c2ecf20Sopenharmony_ci				"type 0x%04x, skipping\n", udev->devnum,
11988c2ecf20Sopenharmony_ci				alts->desc.bInterfaceNumber,
11998c2ecf20Sopenharmony_ci				buffer[3], type);
12008c2ecf20Sopenharmony_ci			return 0;
12018c2ecf20Sopenharmony_ci		}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci		n = 0;
12048c2ecf20Sopenharmony_ci		p = 0;
12058c2ecf20Sopenharmony_ci		len = 8;
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci		if (type == UVC_ITT_CAMERA) {
12088c2ecf20Sopenharmony_ci			n = buflen >= 15 ? buffer[14] : 0;
12098c2ecf20Sopenharmony_ci			len = 15;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci		} else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) {
12128c2ecf20Sopenharmony_ci			n = buflen >= 9 ? buffer[8] : 0;
12138c2ecf20Sopenharmony_ci			p = buflen >= 10 + n ? buffer[9+n] : 0;
12148c2ecf20Sopenharmony_ci			len = 10;
12158c2ecf20Sopenharmony_ci		}
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci		if (buflen < len + n + p) {
12188c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
12198c2ecf20Sopenharmony_ci				"interface %d INPUT_TERMINAL error\n",
12208c2ecf20Sopenharmony_ci				udev->devnum, alts->desc.bInterfaceNumber);
12218c2ecf20Sopenharmony_ci			return -EINVAL;
12228c2ecf20Sopenharmony_ci		}
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci		term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3],
12258c2ecf20Sopenharmony_ci					1, n + p);
12268c2ecf20Sopenharmony_ci		if (term == NULL)
12278c2ecf20Sopenharmony_ci			return -ENOMEM;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci		if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) {
12308c2ecf20Sopenharmony_ci			term->camera.bControlSize = n;
12318c2ecf20Sopenharmony_ci			term->camera.bmControls = (u8 *)term + sizeof(*term);
12328c2ecf20Sopenharmony_ci			term->camera.wObjectiveFocalLengthMin =
12338c2ecf20Sopenharmony_ci				get_unaligned_le16(&buffer[8]);
12348c2ecf20Sopenharmony_ci			term->camera.wObjectiveFocalLengthMax =
12358c2ecf20Sopenharmony_ci				get_unaligned_le16(&buffer[10]);
12368c2ecf20Sopenharmony_ci			term->camera.wOcularFocalLength =
12378c2ecf20Sopenharmony_ci				get_unaligned_le16(&buffer[12]);
12388c2ecf20Sopenharmony_ci			memcpy(term->camera.bmControls, &buffer[15], n);
12398c2ecf20Sopenharmony_ci		} else if (UVC_ENTITY_TYPE(term) ==
12408c2ecf20Sopenharmony_ci			   UVC_ITT_MEDIA_TRANSPORT_INPUT) {
12418c2ecf20Sopenharmony_ci			term->media.bControlSize = n;
12428c2ecf20Sopenharmony_ci			term->media.bmControls = (u8 *)term + sizeof(*term);
12438c2ecf20Sopenharmony_ci			term->media.bTransportModeSize = p;
12448c2ecf20Sopenharmony_ci			term->media.bmTransportModes = (u8 *)term
12458c2ecf20Sopenharmony_ci						     + sizeof(*term) + n;
12468c2ecf20Sopenharmony_ci			memcpy(term->media.bmControls, &buffer[9], n);
12478c2ecf20Sopenharmony_ci			memcpy(term->media.bmTransportModes, &buffer[10+n], p);
12488c2ecf20Sopenharmony_ci		}
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci		if (buffer[7] == 0 ||
12518c2ecf20Sopenharmony_ci		    usb_string(udev, buffer[7], term->name, sizeof(term->name)) < 0) {
12528c2ecf20Sopenharmony_ci			if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA)
12538c2ecf20Sopenharmony_ci				sprintf(term->name, "Camera %u", buffer[3]);
12548c2ecf20Sopenharmony_ci			if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT)
12558c2ecf20Sopenharmony_ci				sprintf(term->name, "Media %u", buffer[3]);
12568c2ecf20Sopenharmony_ci			else
12578c2ecf20Sopenharmony_ci				sprintf(term->name, "Input %u", buffer[3]);
12588c2ecf20Sopenharmony_ci		}
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci		list_add_tail(&term->list, &dev->entities);
12618c2ecf20Sopenharmony_ci		break;
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	case UVC_VC_OUTPUT_TERMINAL:
12648c2ecf20Sopenharmony_ci		if (buflen < 9) {
12658c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
12668c2ecf20Sopenharmony_ci				"interface %d OUTPUT_TERMINAL error\n",
12678c2ecf20Sopenharmony_ci				udev->devnum, alts->desc.bInterfaceNumber);
12688c2ecf20Sopenharmony_ci			return -EINVAL;
12698c2ecf20Sopenharmony_ci		}
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci		/* Make sure the terminal type MSB is not null, otherwise it
12728c2ecf20Sopenharmony_ci		 * could be confused with a unit.
12738c2ecf20Sopenharmony_ci		 */
12748c2ecf20Sopenharmony_ci		type = get_unaligned_le16(&buffer[4]);
12758c2ecf20Sopenharmony_ci		if ((type & 0xff00) == 0) {
12768c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
12778c2ecf20Sopenharmony_ci				"interface %d OUTPUT_TERMINAL %d has invalid "
12788c2ecf20Sopenharmony_ci				"type 0x%04x, skipping\n", udev->devnum,
12798c2ecf20Sopenharmony_ci				alts->desc.bInterfaceNumber, buffer[3], type);
12808c2ecf20Sopenharmony_ci			return 0;
12818c2ecf20Sopenharmony_ci		}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci		term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3],
12848c2ecf20Sopenharmony_ci					1, 0);
12858c2ecf20Sopenharmony_ci		if (term == NULL)
12868c2ecf20Sopenharmony_ci			return -ENOMEM;
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci		memcpy(term->baSourceID, &buffer[7], 1);
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci		if (buffer[8] == 0 ||
12918c2ecf20Sopenharmony_ci		    usb_string(udev, buffer[8], term->name, sizeof(term->name)) < 0)
12928c2ecf20Sopenharmony_ci			sprintf(term->name, "Output %u", buffer[3]);
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci		list_add_tail(&term->list, &dev->entities);
12958c2ecf20Sopenharmony_ci		break;
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	case UVC_VC_SELECTOR_UNIT:
12988c2ecf20Sopenharmony_ci		p = buflen >= 5 ? buffer[4] : 0;
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci		if (buflen < 5 || buflen < 6 + p) {
13018c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
13028c2ecf20Sopenharmony_ci				"interface %d SELECTOR_UNIT error\n",
13038c2ecf20Sopenharmony_ci				udev->devnum, alts->desc.bInterfaceNumber);
13048c2ecf20Sopenharmony_ci			return -EINVAL;
13058c2ecf20Sopenharmony_ci		}
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci		unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0);
13088c2ecf20Sopenharmony_ci		if (unit == NULL)
13098c2ecf20Sopenharmony_ci			return -ENOMEM;
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci		memcpy(unit->baSourceID, &buffer[5], p);
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci		if (buffer[5+p] == 0 ||
13148c2ecf20Sopenharmony_ci		    usb_string(udev, buffer[5+p], unit->name, sizeof(unit->name)) < 0)
13158c2ecf20Sopenharmony_ci			sprintf(unit->name, "Selector %u", buffer[3]);
13168c2ecf20Sopenharmony_ci
13178c2ecf20Sopenharmony_ci		list_add_tail(&unit->list, &dev->entities);
13188c2ecf20Sopenharmony_ci		break;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	case UVC_VC_PROCESSING_UNIT:
13218c2ecf20Sopenharmony_ci		n = buflen >= 8 ? buffer[7] : 0;
13228c2ecf20Sopenharmony_ci		p = dev->uvc_version >= 0x0110 ? 10 : 9;
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci		if (buflen < p + n) {
13258c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
13268c2ecf20Sopenharmony_ci				"interface %d PROCESSING_UNIT error\n",
13278c2ecf20Sopenharmony_ci				udev->devnum, alts->desc.bInterfaceNumber);
13288c2ecf20Sopenharmony_ci			return -EINVAL;
13298c2ecf20Sopenharmony_ci		}
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci		unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n);
13328c2ecf20Sopenharmony_ci		if (unit == NULL)
13338c2ecf20Sopenharmony_ci			return -ENOMEM;
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci		memcpy(unit->baSourceID, &buffer[4], 1);
13368c2ecf20Sopenharmony_ci		unit->processing.wMaxMultiplier =
13378c2ecf20Sopenharmony_ci			get_unaligned_le16(&buffer[5]);
13388c2ecf20Sopenharmony_ci		unit->processing.bControlSize = buffer[7];
13398c2ecf20Sopenharmony_ci		unit->processing.bmControls = (u8 *)unit + sizeof(*unit);
13408c2ecf20Sopenharmony_ci		memcpy(unit->processing.bmControls, &buffer[8], n);
13418c2ecf20Sopenharmony_ci		if (dev->uvc_version >= 0x0110)
13428c2ecf20Sopenharmony_ci			unit->processing.bmVideoStandards = buffer[9+n];
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci		if (buffer[8+n] == 0 ||
13458c2ecf20Sopenharmony_ci		    usb_string(udev, buffer[8+n], unit->name, sizeof(unit->name)) < 0)
13468c2ecf20Sopenharmony_ci			sprintf(unit->name, "Processing %u", buffer[3]);
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci		list_add_tail(&unit->list, &dev->entities);
13498c2ecf20Sopenharmony_ci		break;
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	case UVC_VC_EXTENSION_UNIT:
13528c2ecf20Sopenharmony_ci		p = buflen >= 22 ? buffer[21] : 0;
13538c2ecf20Sopenharmony_ci		n = buflen >= 24 + p ? buffer[22+p] : 0;
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci		if (buflen < 24 + p + n) {
13568c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol "
13578c2ecf20Sopenharmony_ci				"interface %d EXTENSION_UNIT error\n",
13588c2ecf20Sopenharmony_ci				udev->devnum, alts->desc.bInterfaceNumber);
13598c2ecf20Sopenharmony_ci			return -EINVAL;
13608c2ecf20Sopenharmony_ci		}
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci		unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n);
13638c2ecf20Sopenharmony_ci		if (unit == NULL)
13648c2ecf20Sopenharmony_ci			return -ENOMEM;
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci		memcpy(unit->extension.guidExtensionCode, &buffer[4], 16);
13678c2ecf20Sopenharmony_ci		unit->extension.bNumControls = buffer[20];
13688c2ecf20Sopenharmony_ci		memcpy(unit->baSourceID, &buffer[22], p);
13698c2ecf20Sopenharmony_ci		unit->extension.bControlSize = buffer[22+p];
13708c2ecf20Sopenharmony_ci		unit->extension.bmControls = (u8 *)unit + sizeof(*unit);
13718c2ecf20Sopenharmony_ci		memcpy(unit->extension.bmControls, &buffer[23+p], n);
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci		if (buffer[23+p+n] == 0 ||
13748c2ecf20Sopenharmony_ci		    usb_string(udev, buffer[23+p+n], unit->name, sizeof(unit->name)) < 0)
13758c2ecf20Sopenharmony_ci			sprintf(unit->name, "Extension %u", buffer[3]);
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci		list_add_tail(&unit->list, &dev->entities);
13788c2ecf20Sopenharmony_ci		break;
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	default:
13818c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE "
13828c2ecf20Sopenharmony_ci			"descriptor (%u)\n", buffer[2]);
13838c2ecf20Sopenharmony_ci		break;
13848c2ecf20Sopenharmony_ci	}
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	return 0;
13878c2ecf20Sopenharmony_ci}
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_cistatic int uvc_parse_control(struct uvc_device *dev)
13908c2ecf20Sopenharmony_ci{
13918c2ecf20Sopenharmony_ci	struct usb_host_interface *alts = dev->intf->cur_altsetting;
13928c2ecf20Sopenharmony_ci	unsigned char *buffer = alts->extra;
13938c2ecf20Sopenharmony_ci	int buflen = alts->extralen;
13948c2ecf20Sopenharmony_ci	int ret;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	/* Parse the default alternate setting only, as the UVC specification
13978c2ecf20Sopenharmony_ci	 * defines a single alternate setting, the default alternate setting
13988c2ecf20Sopenharmony_ci	 * zero.
13998c2ecf20Sopenharmony_ci	 */
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	while (buflen > 2) {
14028c2ecf20Sopenharmony_ci		if (uvc_parse_vendor_control(dev, buffer, buflen) ||
14038c2ecf20Sopenharmony_ci		    buffer[1] != USB_DT_CS_INTERFACE)
14048c2ecf20Sopenharmony_ci			goto next_descriptor;
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci		if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0)
14078c2ecf20Sopenharmony_ci			return ret;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_cinext_descriptor:
14108c2ecf20Sopenharmony_ci		buflen -= buffer[0];
14118c2ecf20Sopenharmony_ci		buffer += buffer[0];
14128c2ecf20Sopenharmony_ci	}
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	/* Check if the optional status endpoint is present. Built-in iSight
14158c2ecf20Sopenharmony_ci	 * webcams have an interrupt endpoint but spit proprietary data that
14168c2ecf20Sopenharmony_ci	 * don't conform to the UVC status endpoint messages. Don't try to
14178c2ecf20Sopenharmony_ci	 * handle the interrupt endpoint for those cameras.
14188c2ecf20Sopenharmony_ci	 */
14198c2ecf20Sopenharmony_ci	if (alts->desc.bNumEndpoints == 1 &&
14208c2ecf20Sopenharmony_ci	    !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) {
14218c2ecf20Sopenharmony_ci		struct usb_host_endpoint *ep = &alts->endpoint[0];
14228c2ecf20Sopenharmony_ci		struct usb_endpoint_descriptor *desc = &ep->desc;
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci		if (usb_endpoint_is_int_in(desc) &&
14258c2ecf20Sopenharmony_ci		    le16_to_cpu(desc->wMaxPacketSize) >= 8 &&
14268c2ecf20Sopenharmony_ci		    desc->bInterval != 0) {
14278c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint "
14288c2ecf20Sopenharmony_ci				"(addr %02x).\n", desc->bEndpointAddress);
14298c2ecf20Sopenharmony_ci			dev->int_ep = ep;
14308c2ecf20Sopenharmony_ci		}
14318c2ecf20Sopenharmony_ci	}
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	return 0;
14348c2ecf20Sopenharmony_ci}
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------
14378c2ecf20Sopenharmony_ci * UVC device scan
14388c2ecf20Sopenharmony_ci */
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci/*
14418c2ecf20Sopenharmony_ci * Scan the UVC descriptors to locate a chain starting at an Output Terminal
14428c2ecf20Sopenharmony_ci * and containing the following units:
14438c2ecf20Sopenharmony_ci *
14448c2ecf20Sopenharmony_ci * - one or more Output Terminals (USB Streaming or Display)
14458c2ecf20Sopenharmony_ci * - zero or one Processing Unit
14468c2ecf20Sopenharmony_ci * - zero, one or more single-input Selector Units
14478c2ecf20Sopenharmony_ci * - zero or one multiple-input Selector Units, provided all inputs are
14488c2ecf20Sopenharmony_ci *   connected to input terminals
14498c2ecf20Sopenharmony_ci * - zero, one or mode single-input Extension Units
14508c2ecf20Sopenharmony_ci * - one or more Input Terminals (Camera, External or USB Streaming)
14518c2ecf20Sopenharmony_ci *
14528c2ecf20Sopenharmony_ci * The terminal and units must match on of the following structures:
14538c2ecf20Sopenharmony_ci *
14548c2ecf20Sopenharmony_ci * ITT_*(0) -> +---------+    +---------+    +---------+ -> TT_STREAMING(0)
14558c2ecf20Sopenharmony_ci * ...         | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} |    ...
14568c2ecf20Sopenharmony_ci * ITT_*(n) -> +---------+    +---------+    +---------+ -> TT_STREAMING(n)
14578c2ecf20Sopenharmony_ci *
14588c2ecf20Sopenharmony_ci *                 +---------+    +---------+ -> OTT_*(0)
14598c2ecf20Sopenharmony_ci * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} |    ...
14608c2ecf20Sopenharmony_ci *                 +---------+    +---------+ -> OTT_*(n)
14618c2ecf20Sopenharmony_ci *
14628c2ecf20Sopenharmony_ci * The Processing Unit and Extension Units can be in any order. Additional
14638c2ecf20Sopenharmony_ci * Extension Units connected to the main chain as single-unit branches are
14648c2ecf20Sopenharmony_ci * also supported. Single-input Selector Units are ignored.
14658c2ecf20Sopenharmony_ci */
14668c2ecf20Sopenharmony_cistatic int uvc_scan_chain_entity(struct uvc_video_chain *chain,
14678c2ecf20Sopenharmony_ci	struct uvc_entity *entity)
14688c2ecf20Sopenharmony_ci{
14698c2ecf20Sopenharmony_ci	switch (UVC_ENTITY_TYPE(entity)) {
14708c2ecf20Sopenharmony_ci	case UVC_VC_EXTENSION_UNIT:
14718c2ecf20Sopenharmony_ci		if (uvc_trace_param & UVC_TRACE_PROBE)
14728c2ecf20Sopenharmony_ci			printk(KERN_CONT " <- XU %d", entity->id);
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci		if (entity->bNrInPins != 1) {
14758c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more "
14768c2ecf20Sopenharmony_ci				"than 1 input pin.\n", entity->id);
14778c2ecf20Sopenharmony_ci			return -1;
14788c2ecf20Sopenharmony_ci		}
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci		break;
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci	case UVC_VC_PROCESSING_UNIT:
14838c2ecf20Sopenharmony_ci		if (uvc_trace_param & UVC_TRACE_PROBE)
14848c2ecf20Sopenharmony_ci			printk(KERN_CONT " <- PU %d", entity->id);
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci		if (chain->processing != NULL) {
14878c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "Found multiple "
14888c2ecf20Sopenharmony_ci				"Processing Units in chain.\n");
14898c2ecf20Sopenharmony_ci			return -1;
14908c2ecf20Sopenharmony_ci		}
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci		chain->processing = entity;
14938c2ecf20Sopenharmony_ci		break;
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	case UVC_VC_SELECTOR_UNIT:
14968c2ecf20Sopenharmony_ci		if (uvc_trace_param & UVC_TRACE_PROBE)
14978c2ecf20Sopenharmony_ci			printk(KERN_CONT " <- SU %d", entity->id);
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci		/* Single-input selector units are ignored. */
15008c2ecf20Sopenharmony_ci		if (entity->bNrInPins == 1)
15018c2ecf20Sopenharmony_ci			break;
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci		if (chain->selector != NULL) {
15048c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector "
15058c2ecf20Sopenharmony_ci				"Units in chain.\n");
15068c2ecf20Sopenharmony_ci			return -1;
15078c2ecf20Sopenharmony_ci		}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci		chain->selector = entity;
15108c2ecf20Sopenharmony_ci		break;
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	case UVC_ITT_VENDOR_SPECIFIC:
15138c2ecf20Sopenharmony_ci	case UVC_ITT_CAMERA:
15148c2ecf20Sopenharmony_ci	case UVC_ITT_MEDIA_TRANSPORT_INPUT:
15158c2ecf20Sopenharmony_ci		if (uvc_trace_param & UVC_TRACE_PROBE)
15168c2ecf20Sopenharmony_ci			printk(KERN_CONT " <- IT %d\n", entity->id);
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci		break;
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_ci	case UVC_OTT_VENDOR_SPECIFIC:
15218c2ecf20Sopenharmony_ci	case UVC_OTT_DISPLAY:
15228c2ecf20Sopenharmony_ci	case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
15238c2ecf20Sopenharmony_ci		if (uvc_trace_param & UVC_TRACE_PROBE)
15248c2ecf20Sopenharmony_ci			printk(KERN_CONT " OT %d", entity->id);
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci		break;
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	case UVC_TT_STREAMING:
15298c2ecf20Sopenharmony_ci		if (UVC_ENTITY_IS_ITERM(entity)) {
15308c2ecf20Sopenharmony_ci			if (uvc_trace_param & UVC_TRACE_PROBE)
15318c2ecf20Sopenharmony_ci				printk(KERN_CONT " <- IT %d\n", entity->id);
15328c2ecf20Sopenharmony_ci		} else {
15338c2ecf20Sopenharmony_ci			if (uvc_trace_param & UVC_TRACE_PROBE)
15348c2ecf20Sopenharmony_ci				printk(KERN_CONT " OT %d", entity->id);
15358c2ecf20Sopenharmony_ci		}
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci		break;
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	default:
15408c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type "
15418c2ecf20Sopenharmony_ci			"0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
15428c2ecf20Sopenharmony_ci		return -1;
15438c2ecf20Sopenharmony_ci	}
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	list_add_tail(&entity->chain, &chain->entities);
15468c2ecf20Sopenharmony_ci	return 0;
15478c2ecf20Sopenharmony_ci}
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_cistatic int uvc_scan_chain_forward(struct uvc_video_chain *chain,
15508c2ecf20Sopenharmony_ci	struct uvc_entity *entity, struct uvc_entity *prev)
15518c2ecf20Sopenharmony_ci{
15528c2ecf20Sopenharmony_ci	struct uvc_entity *forward;
15538c2ecf20Sopenharmony_ci	int found;
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	/* Forward scan */
15568c2ecf20Sopenharmony_ci	forward = NULL;
15578c2ecf20Sopenharmony_ci	found = 0;
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	while (1) {
15608c2ecf20Sopenharmony_ci		forward = uvc_entity_by_reference(chain->dev, entity->id,
15618c2ecf20Sopenharmony_ci			forward);
15628c2ecf20Sopenharmony_ci		if (forward == NULL)
15638c2ecf20Sopenharmony_ci			break;
15648c2ecf20Sopenharmony_ci		if (forward == prev)
15658c2ecf20Sopenharmony_ci			continue;
15668c2ecf20Sopenharmony_ci		if (forward->chain.next || forward->chain.prev) {
15678c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "Found reference to "
15688c2ecf20Sopenharmony_ci				"entity %d already in chain.\n", forward->id);
15698c2ecf20Sopenharmony_ci			return -EINVAL;
15708c2ecf20Sopenharmony_ci		}
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci		switch (UVC_ENTITY_TYPE(forward)) {
15738c2ecf20Sopenharmony_ci		case UVC_VC_EXTENSION_UNIT:
15748c2ecf20Sopenharmony_ci			if (forward->bNrInPins != 1) {
15758c2ecf20Sopenharmony_ci				uvc_trace(UVC_TRACE_DESCR, "Extension unit %d "
15768c2ecf20Sopenharmony_ci					  "has more than 1 input pin.\n",
15778c2ecf20Sopenharmony_ci					  entity->id);
15788c2ecf20Sopenharmony_ci				return -EINVAL;
15798c2ecf20Sopenharmony_ci			}
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci			/*
15828c2ecf20Sopenharmony_ci			 * Some devices reference an output terminal as the
15838c2ecf20Sopenharmony_ci			 * source of extension units. This is incorrect, as
15848c2ecf20Sopenharmony_ci			 * output terminals only have an input pin, and thus
15858c2ecf20Sopenharmony_ci			 * can't be connected to any entity in the forward
15868c2ecf20Sopenharmony_ci			 * direction. The resulting topology would cause issues
15878c2ecf20Sopenharmony_ci			 * when registering the media controller graph. To
15888c2ecf20Sopenharmony_ci			 * avoid this problem, connect the extension unit to
15898c2ecf20Sopenharmony_ci			 * the source of the output terminal instead.
15908c2ecf20Sopenharmony_ci			 */
15918c2ecf20Sopenharmony_ci			if (UVC_ENTITY_IS_OTERM(entity)) {
15928c2ecf20Sopenharmony_ci				struct uvc_entity *source;
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci				source = uvc_entity_by_id(chain->dev,
15958c2ecf20Sopenharmony_ci							  entity->baSourceID[0]);
15968c2ecf20Sopenharmony_ci				if (!source) {
15978c2ecf20Sopenharmony_ci					uvc_trace(UVC_TRACE_DESCR,
15988c2ecf20Sopenharmony_ci						"Can't connect extension unit %u in chain\n",
15998c2ecf20Sopenharmony_ci						forward->id);
16008c2ecf20Sopenharmony_ci					break;
16018c2ecf20Sopenharmony_ci				}
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci				forward->baSourceID[0] = source->id;
16048c2ecf20Sopenharmony_ci			}
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci			list_add_tail(&forward->chain, &chain->entities);
16078c2ecf20Sopenharmony_ci			if (uvc_trace_param & UVC_TRACE_PROBE) {
16088c2ecf20Sopenharmony_ci				if (!found)
16098c2ecf20Sopenharmony_ci					printk(KERN_CONT " (->");
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci				printk(KERN_CONT " XU %d", forward->id);
16128c2ecf20Sopenharmony_ci				found = 1;
16138c2ecf20Sopenharmony_ci			}
16148c2ecf20Sopenharmony_ci			break;
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci		case UVC_OTT_VENDOR_SPECIFIC:
16178c2ecf20Sopenharmony_ci		case UVC_OTT_DISPLAY:
16188c2ecf20Sopenharmony_ci		case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
16198c2ecf20Sopenharmony_ci		case UVC_TT_STREAMING:
16208c2ecf20Sopenharmony_ci			if (UVC_ENTITY_IS_ITERM(forward)) {
16218c2ecf20Sopenharmony_ci				uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
16228c2ecf20Sopenharmony_ci					"terminal %u.\n", forward->id);
16238c2ecf20Sopenharmony_ci				return -EINVAL;
16248c2ecf20Sopenharmony_ci			}
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci			if (UVC_ENTITY_IS_OTERM(entity)) {
16278c2ecf20Sopenharmony_ci				uvc_trace(UVC_TRACE_DESCR,
16288c2ecf20Sopenharmony_ci					"Unsupported connection between output terminals %u and %u\n",
16298c2ecf20Sopenharmony_ci					entity->id, forward->id);
16308c2ecf20Sopenharmony_ci				break;
16318c2ecf20Sopenharmony_ci			}
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci			list_add_tail(&forward->chain, &chain->entities);
16348c2ecf20Sopenharmony_ci			if (uvc_trace_param & UVC_TRACE_PROBE) {
16358c2ecf20Sopenharmony_ci				if (!found)
16368c2ecf20Sopenharmony_ci					printk(KERN_CONT " (->");
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci				printk(KERN_CONT " OT %d", forward->id);
16398c2ecf20Sopenharmony_ci				found = 1;
16408c2ecf20Sopenharmony_ci			}
16418c2ecf20Sopenharmony_ci			break;
16428c2ecf20Sopenharmony_ci		}
16438c2ecf20Sopenharmony_ci	}
16448c2ecf20Sopenharmony_ci	if (found)
16458c2ecf20Sopenharmony_ci		printk(KERN_CONT ")");
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	return 0;
16488c2ecf20Sopenharmony_ci}
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_cistatic int uvc_scan_chain_backward(struct uvc_video_chain *chain,
16518c2ecf20Sopenharmony_ci	struct uvc_entity **_entity)
16528c2ecf20Sopenharmony_ci{
16538c2ecf20Sopenharmony_ci	struct uvc_entity *entity = *_entity;
16548c2ecf20Sopenharmony_ci	struct uvc_entity *term;
16558c2ecf20Sopenharmony_ci	int id = -EINVAL, i;
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	switch (UVC_ENTITY_TYPE(entity)) {
16588c2ecf20Sopenharmony_ci	case UVC_VC_EXTENSION_UNIT:
16598c2ecf20Sopenharmony_ci	case UVC_VC_PROCESSING_UNIT:
16608c2ecf20Sopenharmony_ci		id = entity->baSourceID[0];
16618c2ecf20Sopenharmony_ci		break;
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci	case UVC_VC_SELECTOR_UNIT:
16648c2ecf20Sopenharmony_ci		/* Single-input selector units are ignored. */
16658c2ecf20Sopenharmony_ci		if (entity->bNrInPins == 1) {
16668c2ecf20Sopenharmony_ci			id = entity->baSourceID[0];
16678c2ecf20Sopenharmony_ci			break;
16688c2ecf20Sopenharmony_ci		}
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci		if (uvc_trace_param & UVC_TRACE_PROBE)
16718c2ecf20Sopenharmony_ci			printk(KERN_CONT " <- IT");
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci		chain->selector = entity;
16748c2ecf20Sopenharmony_ci		for (i = 0; i < entity->bNrInPins; ++i) {
16758c2ecf20Sopenharmony_ci			id = entity->baSourceID[i];
16768c2ecf20Sopenharmony_ci			term = uvc_entity_by_id(chain->dev, id);
16778c2ecf20Sopenharmony_ci			if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
16788c2ecf20Sopenharmony_ci				uvc_trace(UVC_TRACE_DESCR, "Selector unit %d "
16798c2ecf20Sopenharmony_ci					"input %d isn't connected to an "
16808c2ecf20Sopenharmony_ci					"input terminal\n", entity->id, i);
16818c2ecf20Sopenharmony_ci				return -1;
16828c2ecf20Sopenharmony_ci			}
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci			if (term->chain.next || term->chain.prev) {
16858c2ecf20Sopenharmony_ci				uvc_trace(UVC_TRACE_DESCR, "Found reference to "
16868c2ecf20Sopenharmony_ci					"entity %d already in chain.\n",
16878c2ecf20Sopenharmony_ci					term->id);
16888c2ecf20Sopenharmony_ci				return -EINVAL;
16898c2ecf20Sopenharmony_ci			}
16908c2ecf20Sopenharmony_ci
16918c2ecf20Sopenharmony_ci			if (uvc_trace_param & UVC_TRACE_PROBE)
16928c2ecf20Sopenharmony_ci				printk(KERN_CONT " %d", term->id);
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_ci			list_add_tail(&term->chain, &chain->entities);
16958c2ecf20Sopenharmony_ci			uvc_scan_chain_forward(chain, term, entity);
16968c2ecf20Sopenharmony_ci		}
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci		if (uvc_trace_param & UVC_TRACE_PROBE)
16998c2ecf20Sopenharmony_ci			printk(KERN_CONT "\n");
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci		id = 0;
17028c2ecf20Sopenharmony_ci		break;
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci	case UVC_ITT_VENDOR_SPECIFIC:
17058c2ecf20Sopenharmony_ci	case UVC_ITT_CAMERA:
17068c2ecf20Sopenharmony_ci	case UVC_ITT_MEDIA_TRANSPORT_INPUT:
17078c2ecf20Sopenharmony_ci	case UVC_OTT_VENDOR_SPECIFIC:
17088c2ecf20Sopenharmony_ci	case UVC_OTT_DISPLAY:
17098c2ecf20Sopenharmony_ci	case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
17108c2ecf20Sopenharmony_ci	case UVC_TT_STREAMING:
17118c2ecf20Sopenharmony_ci		id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0;
17128c2ecf20Sopenharmony_ci		break;
17138c2ecf20Sopenharmony_ci	}
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ci	if (id <= 0) {
17168c2ecf20Sopenharmony_ci		*_entity = NULL;
17178c2ecf20Sopenharmony_ci		return id;
17188c2ecf20Sopenharmony_ci	}
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci	entity = uvc_entity_by_id(chain->dev, id);
17218c2ecf20Sopenharmony_ci	if (entity == NULL) {
17228c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_DESCR, "Found reference to "
17238c2ecf20Sopenharmony_ci			"unknown entity %d.\n", id);
17248c2ecf20Sopenharmony_ci		return -EINVAL;
17258c2ecf20Sopenharmony_ci	}
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci	*_entity = entity;
17288c2ecf20Sopenharmony_ci	return 0;
17298c2ecf20Sopenharmony_ci}
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_cistatic int uvc_scan_chain(struct uvc_video_chain *chain,
17328c2ecf20Sopenharmony_ci			  struct uvc_entity *term)
17338c2ecf20Sopenharmony_ci{
17348c2ecf20Sopenharmony_ci	struct uvc_entity *entity, *prev;
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci	uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:");
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_ci	entity = term;
17398c2ecf20Sopenharmony_ci	prev = NULL;
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_ci	while (entity != NULL) {
17428c2ecf20Sopenharmony_ci		/* Entity must not be part of an existing chain */
17438c2ecf20Sopenharmony_ci		if (entity->chain.next || entity->chain.prev) {
17448c2ecf20Sopenharmony_ci			uvc_trace(UVC_TRACE_DESCR, "Found reference to "
17458c2ecf20Sopenharmony_ci				"entity %d already in chain.\n", entity->id);
17468c2ecf20Sopenharmony_ci			return -EINVAL;
17478c2ecf20Sopenharmony_ci		}
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci		/* Process entity */
17508c2ecf20Sopenharmony_ci		if (uvc_scan_chain_entity(chain, entity) < 0)
17518c2ecf20Sopenharmony_ci			return -EINVAL;
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci		/* Forward scan */
17548c2ecf20Sopenharmony_ci		if (uvc_scan_chain_forward(chain, entity, prev) < 0)
17558c2ecf20Sopenharmony_ci			return -EINVAL;
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_ci		/* Backward scan */
17588c2ecf20Sopenharmony_ci		prev = entity;
17598c2ecf20Sopenharmony_ci		if (uvc_scan_chain_backward(chain, &entity) < 0)
17608c2ecf20Sopenharmony_ci			return -EINVAL;
17618c2ecf20Sopenharmony_ci	}
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	return 0;
17648c2ecf20Sopenharmony_ci}
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_cistatic unsigned int uvc_print_terms(struct list_head *terms, u16 dir,
17678c2ecf20Sopenharmony_ci		char *buffer)
17688c2ecf20Sopenharmony_ci{
17698c2ecf20Sopenharmony_ci	struct uvc_entity *term;
17708c2ecf20Sopenharmony_ci	unsigned int nterms = 0;
17718c2ecf20Sopenharmony_ci	char *p = buffer;
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci	list_for_each_entry(term, terms, chain) {
17748c2ecf20Sopenharmony_ci		if (!UVC_ENTITY_IS_TERM(term) ||
17758c2ecf20Sopenharmony_ci		    UVC_TERM_DIRECTION(term) != dir)
17768c2ecf20Sopenharmony_ci			continue;
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci		if (nterms)
17798c2ecf20Sopenharmony_ci			p += sprintf(p, ",");
17808c2ecf20Sopenharmony_ci		if (++nterms >= 4) {
17818c2ecf20Sopenharmony_ci			p += sprintf(p, "...");
17828c2ecf20Sopenharmony_ci			break;
17838c2ecf20Sopenharmony_ci		}
17848c2ecf20Sopenharmony_ci		p += sprintf(p, "%u", term->id);
17858c2ecf20Sopenharmony_ci	}
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci	return p - buffer;
17888c2ecf20Sopenharmony_ci}
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_cistatic const char *uvc_print_chain(struct uvc_video_chain *chain)
17918c2ecf20Sopenharmony_ci{
17928c2ecf20Sopenharmony_ci	static char buffer[43];
17938c2ecf20Sopenharmony_ci	char *p = buffer;
17948c2ecf20Sopenharmony_ci
17958c2ecf20Sopenharmony_ci	p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p);
17968c2ecf20Sopenharmony_ci	p += sprintf(p, " -> ");
17978c2ecf20Sopenharmony_ci	uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p);
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci	return buffer;
18008c2ecf20Sopenharmony_ci}
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_cistatic struct uvc_video_chain *uvc_alloc_chain(struct uvc_device *dev)
18038c2ecf20Sopenharmony_ci{
18048c2ecf20Sopenharmony_ci	struct uvc_video_chain *chain;
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci	chain = kzalloc(sizeof(*chain), GFP_KERNEL);
18078c2ecf20Sopenharmony_ci	if (chain == NULL)
18088c2ecf20Sopenharmony_ci		return NULL;
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&chain->entities);
18118c2ecf20Sopenharmony_ci	mutex_init(&chain->ctrl_mutex);
18128c2ecf20Sopenharmony_ci	chain->dev = dev;
18138c2ecf20Sopenharmony_ci	v4l2_prio_init(&chain->prio);
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_ci	return chain;
18168c2ecf20Sopenharmony_ci}
18178c2ecf20Sopenharmony_ci
18188c2ecf20Sopenharmony_ci/*
18198c2ecf20Sopenharmony_ci * Fallback heuristic for devices that don't connect units and terminals in a
18208c2ecf20Sopenharmony_ci * valid chain.
18218c2ecf20Sopenharmony_ci *
18228c2ecf20Sopenharmony_ci * Some devices have invalid baSourceID references, causing uvc_scan_chain()
18238c2ecf20Sopenharmony_ci * to fail, but if we just take the entities we can find and put them together
18248c2ecf20Sopenharmony_ci * in the most sensible chain we can think of, turns out they do work anyway.
18258c2ecf20Sopenharmony_ci * Note: This heuristic assumes there is a single chain.
18268c2ecf20Sopenharmony_ci *
18278c2ecf20Sopenharmony_ci * At the time of writing, devices known to have such a broken chain are
18288c2ecf20Sopenharmony_ci *  - Acer Integrated Camera (5986:055a)
18298c2ecf20Sopenharmony_ci *  - Realtek rtl157a7 (0bda:57a7)
18308c2ecf20Sopenharmony_ci */
18318c2ecf20Sopenharmony_cistatic int uvc_scan_fallback(struct uvc_device *dev)
18328c2ecf20Sopenharmony_ci{
18338c2ecf20Sopenharmony_ci	struct uvc_video_chain *chain;
18348c2ecf20Sopenharmony_ci	struct uvc_entity *iterm = NULL;
18358c2ecf20Sopenharmony_ci	struct uvc_entity *oterm = NULL;
18368c2ecf20Sopenharmony_ci	struct uvc_entity *entity;
18378c2ecf20Sopenharmony_ci	struct uvc_entity *prev;
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci	/*
18408c2ecf20Sopenharmony_ci	 * Start by locating the input and output terminals. We only support
18418c2ecf20Sopenharmony_ci	 * devices with exactly one of each for now.
18428c2ecf20Sopenharmony_ci	 */
18438c2ecf20Sopenharmony_ci	list_for_each_entry(entity, &dev->entities, list) {
18448c2ecf20Sopenharmony_ci		if (UVC_ENTITY_IS_ITERM(entity)) {
18458c2ecf20Sopenharmony_ci			if (iterm)
18468c2ecf20Sopenharmony_ci				return -EINVAL;
18478c2ecf20Sopenharmony_ci			iterm = entity;
18488c2ecf20Sopenharmony_ci		}
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci		if (UVC_ENTITY_IS_OTERM(entity)) {
18518c2ecf20Sopenharmony_ci			if (oterm)
18528c2ecf20Sopenharmony_ci				return -EINVAL;
18538c2ecf20Sopenharmony_ci			oterm = entity;
18548c2ecf20Sopenharmony_ci		}
18558c2ecf20Sopenharmony_ci	}
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_ci	if (iterm == NULL || oterm == NULL)
18588c2ecf20Sopenharmony_ci		return -EINVAL;
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_ci	/* Allocate the chain and fill it. */
18618c2ecf20Sopenharmony_ci	chain = uvc_alloc_chain(dev);
18628c2ecf20Sopenharmony_ci	if (chain == NULL)
18638c2ecf20Sopenharmony_ci		return -ENOMEM;
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	if (uvc_scan_chain_entity(chain, oterm) < 0)
18668c2ecf20Sopenharmony_ci		goto error;
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	prev = oterm;
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_ci	/*
18718c2ecf20Sopenharmony_ci	 * Add all Processing and Extension Units with two pads. The order
18728c2ecf20Sopenharmony_ci	 * doesn't matter much, use reverse list traversal to connect units in
18738c2ecf20Sopenharmony_ci	 * UVC descriptor order as we build the chain from output to input. This
18748c2ecf20Sopenharmony_ci	 * leads to units appearing in the order meant by the manufacturer for
18758c2ecf20Sopenharmony_ci	 * the cameras known to require this heuristic.
18768c2ecf20Sopenharmony_ci	 */
18778c2ecf20Sopenharmony_ci	list_for_each_entry_reverse(entity, &dev->entities, list) {
18788c2ecf20Sopenharmony_ci		if (entity->type != UVC_VC_PROCESSING_UNIT &&
18798c2ecf20Sopenharmony_ci		    entity->type != UVC_VC_EXTENSION_UNIT)
18808c2ecf20Sopenharmony_ci			continue;
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ci		if (entity->num_pads != 2)
18838c2ecf20Sopenharmony_ci			continue;
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci		if (uvc_scan_chain_entity(chain, entity) < 0)
18868c2ecf20Sopenharmony_ci			goto error;
18878c2ecf20Sopenharmony_ci
18888c2ecf20Sopenharmony_ci		prev->baSourceID[0] = entity->id;
18898c2ecf20Sopenharmony_ci		prev = entity;
18908c2ecf20Sopenharmony_ci	}
18918c2ecf20Sopenharmony_ci
18928c2ecf20Sopenharmony_ci	if (uvc_scan_chain_entity(chain, iterm) < 0)
18938c2ecf20Sopenharmony_ci		goto error;
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci	prev->baSourceID[0] = iterm->id;
18968c2ecf20Sopenharmony_ci
18978c2ecf20Sopenharmony_ci	list_add_tail(&chain->list, &dev->chains);
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_ci	uvc_trace(UVC_TRACE_PROBE,
19008c2ecf20Sopenharmony_ci		  "Found a video chain by fallback heuristic (%s).\n",
19018c2ecf20Sopenharmony_ci		  uvc_print_chain(chain));
19028c2ecf20Sopenharmony_ci
19038c2ecf20Sopenharmony_ci	return 0;
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_cierror:
19068c2ecf20Sopenharmony_ci	kfree(chain);
19078c2ecf20Sopenharmony_ci	return -EINVAL;
19088c2ecf20Sopenharmony_ci}
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci/*
19118c2ecf20Sopenharmony_ci * Scan the device for video chains and register video devices.
19128c2ecf20Sopenharmony_ci *
19138c2ecf20Sopenharmony_ci * Chains are scanned starting at their output terminals and walked backwards.
19148c2ecf20Sopenharmony_ci */
19158c2ecf20Sopenharmony_cistatic int uvc_scan_device(struct uvc_device *dev)
19168c2ecf20Sopenharmony_ci{
19178c2ecf20Sopenharmony_ci	struct uvc_video_chain *chain;
19188c2ecf20Sopenharmony_ci	struct uvc_entity *term;
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci	list_for_each_entry(term, &dev->entities, list) {
19218c2ecf20Sopenharmony_ci		if (!UVC_ENTITY_IS_OTERM(term))
19228c2ecf20Sopenharmony_ci			continue;
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci		/* If the terminal is already included in a chain, skip it.
19258c2ecf20Sopenharmony_ci		 * This can happen for chains that have multiple output
19268c2ecf20Sopenharmony_ci		 * terminals, where all output terminals beside the first one
19278c2ecf20Sopenharmony_ci		 * will be inserted in the chain in forward scans.
19288c2ecf20Sopenharmony_ci		 */
19298c2ecf20Sopenharmony_ci		if (term->chain.next || term->chain.prev)
19308c2ecf20Sopenharmony_ci			continue;
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_ci		chain = uvc_alloc_chain(dev);
19338c2ecf20Sopenharmony_ci		if (chain == NULL)
19348c2ecf20Sopenharmony_ci			return -ENOMEM;
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci		term->flags |= UVC_ENTITY_FLAG_DEFAULT;
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci		if (uvc_scan_chain(chain, term) < 0) {
19398c2ecf20Sopenharmony_ci			kfree(chain);
19408c2ecf20Sopenharmony_ci			continue;
19418c2ecf20Sopenharmony_ci		}
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",
19448c2ecf20Sopenharmony_ci			  uvc_print_chain(chain));
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_ci		list_add_tail(&chain->list, &dev->chains);
19478c2ecf20Sopenharmony_ci	}
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci	if (list_empty(&dev->chains))
19508c2ecf20Sopenharmony_ci		uvc_scan_fallback(dev);
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_ci	if (list_empty(&dev->chains)) {
19538c2ecf20Sopenharmony_ci		uvc_printk(KERN_INFO, "No valid video chain found.\n");
19548c2ecf20Sopenharmony_ci		return -1;
19558c2ecf20Sopenharmony_ci	}
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci	return 0;
19588c2ecf20Sopenharmony_ci}
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------
19618c2ecf20Sopenharmony_ci * Video device registration and unregistration
19628c2ecf20Sopenharmony_ci */
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_ci/*
19658c2ecf20Sopenharmony_ci * Delete the UVC device.
19668c2ecf20Sopenharmony_ci *
19678c2ecf20Sopenharmony_ci * Called by the kernel when the last reference to the uvc_device structure
19688c2ecf20Sopenharmony_ci * is released.
19698c2ecf20Sopenharmony_ci *
19708c2ecf20Sopenharmony_ci * As this function is called after or during disconnect(), all URBs have
19718c2ecf20Sopenharmony_ci * already been cancelled by the USB core. There is no need to kill the
19728c2ecf20Sopenharmony_ci * interrupt URB manually.
19738c2ecf20Sopenharmony_ci */
19748c2ecf20Sopenharmony_cistatic void uvc_delete(struct kref *kref)
19758c2ecf20Sopenharmony_ci{
19768c2ecf20Sopenharmony_ci	struct uvc_device *dev = container_of(kref, struct uvc_device, ref);
19778c2ecf20Sopenharmony_ci	struct list_head *p, *n;
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci	uvc_status_cleanup(dev);
19808c2ecf20Sopenharmony_ci	uvc_ctrl_cleanup_device(dev);
19818c2ecf20Sopenharmony_ci
19828c2ecf20Sopenharmony_ci	usb_put_intf(dev->intf);
19838c2ecf20Sopenharmony_ci	usb_put_dev(dev->udev);
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
19868c2ecf20Sopenharmony_ci	media_device_cleanup(&dev->mdev);
19878c2ecf20Sopenharmony_ci#endif
19888c2ecf20Sopenharmony_ci
19898c2ecf20Sopenharmony_ci	list_for_each_safe(p, n, &dev->chains) {
19908c2ecf20Sopenharmony_ci		struct uvc_video_chain *chain;
19918c2ecf20Sopenharmony_ci		chain = list_entry(p, struct uvc_video_chain, list);
19928c2ecf20Sopenharmony_ci		kfree(chain);
19938c2ecf20Sopenharmony_ci	}
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	list_for_each_safe(p, n, &dev->entities) {
19968c2ecf20Sopenharmony_ci		struct uvc_entity *entity;
19978c2ecf20Sopenharmony_ci		entity = list_entry(p, struct uvc_entity, list);
19988c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
19998c2ecf20Sopenharmony_ci		uvc_mc_cleanup_entity(entity);
20008c2ecf20Sopenharmony_ci#endif
20018c2ecf20Sopenharmony_ci		kfree(entity);
20028c2ecf20Sopenharmony_ci	}
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci	list_for_each_safe(p, n, &dev->streams) {
20058c2ecf20Sopenharmony_ci		struct uvc_streaming *streaming;
20068c2ecf20Sopenharmony_ci		streaming = list_entry(p, struct uvc_streaming, list);
20078c2ecf20Sopenharmony_ci		usb_driver_release_interface(&uvc_driver.driver,
20088c2ecf20Sopenharmony_ci			streaming->intf);
20098c2ecf20Sopenharmony_ci		uvc_stream_delete(streaming);
20108c2ecf20Sopenharmony_ci	}
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_ci	kfree(dev);
20138c2ecf20Sopenharmony_ci}
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_cistatic void uvc_release(struct video_device *vdev)
20168c2ecf20Sopenharmony_ci{
20178c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = video_get_drvdata(vdev);
20188c2ecf20Sopenharmony_ci	struct uvc_device *dev = stream->dev;
20198c2ecf20Sopenharmony_ci
20208c2ecf20Sopenharmony_ci	kref_put(&dev->ref, uvc_delete);
20218c2ecf20Sopenharmony_ci}
20228c2ecf20Sopenharmony_ci
20238c2ecf20Sopenharmony_ci/*
20248c2ecf20Sopenharmony_ci * Unregister the video devices.
20258c2ecf20Sopenharmony_ci */
20268c2ecf20Sopenharmony_cistatic void uvc_unregister_video(struct uvc_device *dev)
20278c2ecf20Sopenharmony_ci{
20288c2ecf20Sopenharmony_ci	struct uvc_streaming *stream;
20298c2ecf20Sopenharmony_ci
20308c2ecf20Sopenharmony_ci	list_for_each_entry(stream, &dev->streams, list) {
20318c2ecf20Sopenharmony_ci		if (!video_is_registered(&stream->vdev))
20328c2ecf20Sopenharmony_ci			continue;
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci		video_unregister_device(&stream->vdev);
20358c2ecf20Sopenharmony_ci		video_unregister_device(&stream->meta.vdev);
20368c2ecf20Sopenharmony_ci
20378c2ecf20Sopenharmony_ci		uvc_debugfs_cleanup_stream(stream);
20388c2ecf20Sopenharmony_ci	}
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci	uvc_status_unregister(dev);
20418c2ecf20Sopenharmony_ci
20428c2ecf20Sopenharmony_ci	if (dev->vdev.dev)
20438c2ecf20Sopenharmony_ci		v4l2_device_unregister(&dev->vdev);
20448c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
20458c2ecf20Sopenharmony_ci	if (media_devnode_is_registered(dev->mdev.devnode))
20468c2ecf20Sopenharmony_ci		media_device_unregister(&dev->mdev);
20478c2ecf20Sopenharmony_ci#endif
20488c2ecf20Sopenharmony_ci}
20498c2ecf20Sopenharmony_ci
20508c2ecf20Sopenharmony_ciint uvc_register_video_device(struct uvc_device *dev,
20518c2ecf20Sopenharmony_ci			      struct uvc_streaming *stream,
20528c2ecf20Sopenharmony_ci			      struct video_device *vdev,
20538c2ecf20Sopenharmony_ci			      struct uvc_video_queue *queue,
20548c2ecf20Sopenharmony_ci			      enum v4l2_buf_type type,
20558c2ecf20Sopenharmony_ci			      const struct v4l2_file_operations *fops,
20568c2ecf20Sopenharmony_ci			      const struct v4l2_ioctl_ops *ioctl_ops)
20578c2ecf20Sopenharmony_ci{
20588c2ecf20Sopenharmony_ci	int ret;
20598c2ecf20Sopenharmony_ci
20608c2ecf20Sopenharmony_ci	/* Initialize the video buffers queue. */
20618c2ecf20Sopenharmony_ci	ret = uvc_queue_init(queue, type, !uvc_no_drop_param);
20628c2ecf20Sopenharmony_ci	if (ret)
20638c2ecf20Sopenharmony_ci		return ret;
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_ci	/* Register the device with V4L. */
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci	/*
20688c2ecf20Sopenharmony_ci	 * We already hold a reference to dev->udev. The video device will be
20698c2ecf20Sopenharmony_ci	 * unregistered before the reference is released, so we don't need to
20708c2ecf20Sopenharmony_ci	 * get another one.
20718c2ecf20Sopenharmony_ci	 */
20728c2ecf20Sopenharmony_ci	vdev->v4l2_dev = &dev->vdev;
20738c2ecf20Sopenharmony_ci	vdev->fops = fops;
20748c2ecf20Sopenharmony_ci	vdev->ioctl_ops = ioctl_ops;
20758c2ecf20Sopenharmony_ci	vdev->release = uvc_release;
20768c2ecf20Sopenharmony_ci	vdev->prio = &stream->chain->prio;
20778c2ecf20Sopenharmony_ci	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
20788c2ecf20Sopenharmony_ci		vdev->vfl_dir = VFL_DIR_TX;
20798c2ecf20Sopenharmony_ci	else
20808c2ecf20Sopenharmony_ci		vdev->vfl_dir = VFL_DIR_RX;
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci	switch (type) {
20838c2ecf20Sopenharmony_ci	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
20848c2ecf20Sopenharmony_ci	default:
20858c2ecf20Sopenharmony_ci		vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
20868c2ecf20Sopenharmony_ci		break;
20878c2ecf20Sopenharmony_ci	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
20888c2ecf20Sopenharmony_ci		vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
20898c2ecf20Sopenharmony_ci		break;
20908c2ecf20Sopenharmony_ci	case V4L2_BUF_TYPE_META_CAPTURE:
20918c2ecf20Sopenharmony_ci		vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
20928c2ecf20Sopenharmony_ci		break;
20938c2ecf20Sopenharmony_ci	}
20948c2ecf20Sopenharmony_ci
20958c2ecf20Sopenharmony_ci	strscpy(vdev->name, dev->name, sizeof(vdev->name));
20968c2ecf20Sopenharmony_ci
20978c2ecf20Sopenharmony_ci	/*
20988c2ecf20Sopenharmony_ci	 * Set the driver data before calling video_register_device, otherwise
20998c2ecf20Sopenharmony_ci	 * the file open() handler might race us.
21008c2ecf20Sopenharmony_ci	 */
21018c2ecf20Sopenharmony_ci	video_set_drvdata(vdev, stream);
21028c2ecf20Sopenharmony_ci
21038c2ecf20Sopenharmony_ci	ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
21048c2ecf20Sopenharmony_ci	if (ret < 0) {
21058c2ecf20Sopenharmony_ci		uvc_printk(KERN_ERR, "Failed to register %s device (%d).\n",
21068c2ecf20Sopenharmony_ci			   v4l2_type_names[type], ret);
21078c2ecf20Sopenharmony_ci		return ret;
21088c2ecf20Sopenharmony_ci	}
21098c2ecf20Sopenharmony_ci
21108c2ecf20Sopenharmony_ci	kref_get(&dev->ref);
21118c2ecf20Sopenharmony_ci	return 0;
21128c2ecf20Sopenharmony_ci}
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_cistatic int uvc_register_video(struct uvc_device *dev,
21158c2ecf20Sopenharmony_ci		struct uvc_streaming *stream)
21168c2ecf20Sopenharmony_ci{
21178c2ecf20Sopenharmony_ci	int ret;
21188c2ecf20Sopenharmony_ci
21198c2ecf20Sopenharmony_ci	/* Initialize the streaming interface with default parameters. */
21208c2ecf20Sopenharmony_ci	ret = uvc_video_init(stream);
21218c2ecf20Sopenharmony_ci	if (ret < 0) {
21228c2ecf20Sopenharmony_ci		uvc_printk(KERN_ERR, "Failed to initialize the device (%d).\n",
21238c2ecf20Sopenharmony_ci			   ret);
21248c2ecf20Sopenharmony_ci		return ret;
21258c2ecf20Sopenharmony_ci	}
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
21288c2ecf20Sopenharmony_ci		stream->chain->caps |= V4L2_CAP_VIDEO_CAPTURE
21298c2ecf20Sopenharmony_ci			| V4L2_CAP_META_CAPTURE;
21308c2ecf20Sopenharmony_ci	else
21318c2ecf20Sopenharmony_ci		stream->chain->caps |= V4L2_CAP_VIDEO_OUTPUT;
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_ci	uvc_debugfs_init_stream(stream);
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci	/* Register the device with V4L. */
21368c2ecf20Sopenharmony_ci	return uvc_register_video_device(dev, stream, &stream->vdev,
21378c2ecf20Sopenharmony_ci					 &stream->queue, stream->type,
21388c2ecf20Sopenharmony_ci					 &uvc_fops, &uvc_ioctl_ops);
21398c2ecf20Sopenharmony_ci}
21408c2ecf20Sopenharmony_ci
21418c2ecf20Sopenharmony_ci/*
21428c2ecf20Sopenharmony_ci * Register all video devices in all chains.
21438c2ecf20Sopenharmony_ci */
21448c2ecf20Sopenharmony_cistatic int uvc_register_terms(struct uvc_device *dev,
21458c2ecf20Sopenharmony_ci	struct uvc_video_chain *chain)
21468c2ecf20Sopenharmony_ci{
21478c2ecf20Sopenharmony_ci	struct uvc_streaming *stream;
21488c2ecf20Sopenharmony_ci	struct uvc_entity *term;
21498c2ecf20Sopenharmony_ci	int ret;
21508c2ecf20Sopenharmony_ci
21518c2ecf20Sopenharmony_ci	list_for_each_entry(term, &chain->entities, chain) {
21528c2ecf20Sopenharmony_ci		if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)
21538c2ecf20Sopenharmony_ci			continue;
21548c2ecf20Sopenharmony_ci
21558c2ecf20Sopenharmony_ci		stream = uvc_stream_by_id(dev, term->id);
21568c2ecf20Sopenharmony_ci		if (stream == NULL) {
21578c2ecf20Sopenharmony_ci			uvc_printk(KERN_INFO, "No streaming interface found "
21588c2ecf20Sopenharmony_ci				   "for terminal %u.", term->id);
21598c2ecf20Sopenharmony_ci			continue;
21608c2ecf20Sopenharmony_ci		}
21618c2ecf20Sopenharmony_ci
21628c2ecf20Sopenharmony_ci		stream->chain = chain;
21638c2ecf20Sopenharmony_ci		ret = uvc_register_video(dev, stream);
21648c2ecf20Sopenharmony_ci		if (ret < 0)
21658c2ecf20Sopenharmony_ci			return ret;
21668c2ecf20Sopenharmony_ci
21678c2ecf20Sopenharmony_ci		/* Register a metadata node, but ignore a possible failure,
21688c2ecf20Sopenharmony_ci		 * complete registration of video nodes anyway.
21698c2ecf20Sopenharmony_ci		 */
21708c2ecf20Sopenharmony_ci		uvc_meta_register(stream);
21718c2ecf20Sopenharmony_ci
21728c2ecf20Sopenharmony_ci		term->vdev = &stream->vdev;
21738c2ecf20Sopenharmony_ci	}
21748c2ecf20Sopenharmony_ci
21758c2ecf20Sopenharmony_ci	return 0;
21768c2ecf20Sopenharmony_ci}
21778c2ecf20Sopenharmony_ci
21788c2ecf20Sopenharmony_cistatic int uvc_register_chains(struct uvc_device *dev)
21798c2ecf20Sopenharmony_ci{
21808c2ecf20Sopenharmony_ci	struct uvc_video_chain *chain;
21818c2ecf20Sopenharmony_ci	int ret;
21828c2ecf20Sopenharmony_ci
21838c2ecf20Sopenharmony_ci	list_for_each_entry(chain, &dev->chains, list) {
21848c2ecf20Sopenharmony_ci		ret = uvc_register_terms(dev, chain);
21858c2ecf20Sopenharmony_ci		if (ret < 0)
21868c2ecf20Sopenharmony_ci			return ret;
21878c2ecf20Sopenharmony_ci
21888c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
21898c2ecf20Sopenharmony_ci		ret = uvc_mc_register_entities(chain);
21908c2ecf20Sopenharmony_ci		if (ret < 0)
21918c2ecf20Sopenharmony_ci			uvc_printk(KERN_INFO,
21928c2ecf20Sopenharmony_ci				   "Failed to register entities (%d).\n", ret);
21938c2ecf20Sopenharmony_ci#endif
21948c2ecf20Sopenharmony_ci	}
21958c2ecf20Sopenharmony_ci
21968c2ecf20Sopenharmony_ci	return 0;
21978c2ecf20Sopenharmony_ci}
21988c2ecf20Sopenharmony_ci
21998c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------
22008c2ecf20Sopenharmony_ci * USB probe, disconnect, suspend and resume
22018c2ecf20Sopenharmony_ci */
22028c2ecf20Sopenharmony_ci
22038c2ecf20Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_none = { 0 };
22048c2ecf20Sopenharmony_ci
22058c2ecf20Sopenharmony_cistatic int uvc_probe(struct usb_interface *intf,
22068c2ecf20Sopenharmony_ci		     const struct usb_device_id *id)
22078c2ecf20Sopenharmony_ci{
22088c2ecf20Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(intf);
22098c2ecf20Sopenharmony_ci	struct uvc_device *dev;
22108c2ecf20Sopenharmony_ci	const struct uvc_device_info *info =
22118c2ecf20Sopenharmony_ci		(const struct uvc_device_info *)id->driver_info;
22128c2ecf20Sopenharmony_ci	int function;
22138c2ecf20Sopenharmony_ci	int ret;
22148c2ecf20Sopenharmony_ci
22158c2ecf20Sopenharmony_ci	if (id->idVendor && id->idProduct)
22168c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
22178c2ecf20Sopenharmony_ci				"(%04x:%04x)\n", udev->devpath, id->idVendor,
22188c2ecf20Sopenharmony_ci				id->idProduct);
22198c2ecf20Sopenharmony_ci	else
22208c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
22218c2ecf20Sopenharmony_ci				udev->devpath);
22228c2ecf20Sopenharmony_ci
22238c2ecf20Sopenharmony_ci	/* Allocate memory for the device and initialize it. */
22248c2ecf20Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
22258c2ecf20Sopenharmony_ci	if (dev == NULL)
22268c2ecf20Sopenharmony_ci		return -ENOMEM;
22278c2ecf20Sopenharmony_ci
22288c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dev->entities);
22298c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dev->chains);
22308c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dev->streams);
22318c2ecf20Sopenharmony_ci	kref_init(&dev->ref);
22328c2ecf20Sopenharmony_ci	atomic_set(&dev->nmappings, 0);
22338c2ecf20Sopenharmony_ci	mutex_init(&dev->lock);
22348c2ecf20Sopenharmony_ci
22358c2ecf20Sopenharmony_ci	dev->udev = usb_get_dev(udev);
22368c2ecf20Sopenharmony_ci	dev->intf = usb_get_intf(intf);
22378c2ecf20Sopenharmony_ci	dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
22388c2ecf20Sopenharmony_ci	dev->info = info ? info : &uvc_quirk_none;
22398c2ecf20Sopenharmony_ci	dev->quirks = uvc_quirks_param == -1
22408c2ecf20Sopenharmony_ci		    ? dev->info->quirks : uvc_quirks_param;
22418c2ecf20Sopenharmony_ci
22428c2ecf20Sopenharmony_ci	if (udev->product != NULL)
22438c2ecf20Sopenharmony_ci		strscpy(dev->name, udev->product, sizeof(dev->name));
22448c2ecf20Sopenharmony_ci	else
22458c2ecf20Sopenharmony_ci		snprintf(dev->name, sizeof(dev->name),
22468c2ecf20Sopenharmony_ci			 "UVC Camera (%04x:%04x)",
22478c2ecf20Sopenharmony_ci			 le16_to_cpu(udev->descriptor.idVendor),
22488c2ecf20Sopenharmony_ci			 le16_to_cpu(udev->descriptor.idProduct));
22498c2ecf20Sopenharmony_ci
22508c2ecf20Sopenharmony_ci	/*
22518c2ecf20Sopenharmony_ci	 * Add iFunction or iInterface to names when available as additional
22528c2ecf20Sopenharmony_ci	 * distinguishers between interfaces. iFunction is prioritized over
22538c2ecf20Sopenharmony_ci	 * iInterface which matches Windows behavior at the point of writing.
22548c2ecf20Sopenharmony_ci	 */
22558c2ecf20Sopenharmony_ci	if (intf->intf_assoc && intf->intf_assoc->iFunction != 0)
22568c2ecf20Sopenharmony_ci		function = intf->intf_assoc->iFunction;
22578c2ecf20Sopenharmony_ci	else
22588c2ecf20Sopenharmony_ci		function = intf->cur_altsetting->desc.iInterface;
22598c2ecf20Sopenharmony_ci	if (function != 0) {
22608c2ecf20Sopenharmony_ci		size_t len;
22618c2ecf20Sopenharmony_ci
22628c2ecf20Sopenharmony_ci		strlcat(dev->name, ": ", sizeof(dev->name));
22638c2ecf20Sopenharmony_ci		len = strlen(dev->name);
22648c2ecf20Sopenharmony_ci		usb_string(udev, function, dev->name + len,
22658c2ecf20Sopenharmony_ci			   sizeof(dev->name) - len);
22668c2ecf20Sopenharmony_ci	}
22678c2ecf20Sopenharmony_ci
22688c2ecf20Sopenharmony_ci	/* Initialize the media device. */
22698c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
22708c2ecf20Sopenharmony_ci	dev->mdev.dev = &intf->dev;
22718c2ecf20Sopenharmony_ci	strscpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
22728c2ecf20Sopenharmony_ci	if (udev->serial)
22738c2ecf20Sopenharmony_ci		strscpy(dev->mdev.serial, udev->serial,
22748c2ecf20Sopenharmony_ci			sizeof(dev->mdev.serial));
22758c2ecf20Sopenharmony_ci	usb_make_path(udev, dev->mdev.bus_info, sizeof(dev->mdev.bus_info));
22768c2ecf20Sopenharmony_ci	dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
22778c2ecf20Sopenharmony_ci	media_device_init(&dev->mdev);
22788c2ecf20Sopenharmony_ci
22798c2ecf20Sopenharmony_ci	dev->vdev.mdev = &dev->mdev;
22808c2ecf20Sopenharmony_ci#endif
22818c2ecf20Sopenharmony_ci
22828c2ecf20Sopenharmony_ci	/* Parse the Video Class control descriptor. */
22838c2ecf20Sopenharmony_ci	if (uvc_parse_control(dev) < 0) {
22848c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC "
22858c2ecf20Sopenharmony_ci			"descriptors.\n");
22868c2ecf20Sopenharmony_ci		goto error;
22878c2ecf20Sopenharmony_ci	}
22888c2ecf20Sopenharmony_ci
22898c2ecf20Sopenharmony_ci	uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n",
22908c2ecf20Sopenharmony_ci		dev->uvc_version >> 8, dev->uvc_version & 0xff,
22918c2ecf20Sopenharmony_ci		udev->product ? udev->product : "<unnamed>",
22928c2ecf20Sopenharmony_ci		le16_to_cpu(udev->descriptor.idVendor),
22938c2ecf20Sopenharmony_ci		le16_to_cpu(udev->descriptor.idProduct));
22948c2ecf20Sopenharmony_ci
22958c2ecf20Sopenharmony_ci	if (dev->quirks != dev->info->quirks) {
22968c2ecf20Sopenharmony_ci		uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module "
22978c2ecf20Sopenharmony_ci			"parameter for testing purpose.\n", dev->quirks);
22988c2ecf20Sopenharmony_ci		uvc_printk(KERN_INFO, "Please report required quirks to the "
22998c2ecf20Sopenharmony_ci			"linux-uvc-devel mailing list.\n");
23008c2ecf20Sopenharmony_ci	}
23018c2ecf20Sopenharmony_ci
23028c2ecf20Sopenharmony_ci	/* Register the V4L2 device. */
23038c2ecf20Sopenharmony_ci	if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
23048c2ecf20Sopenharmony_ci		goto error;
23058c2ecf20Sopenharmony_ci
23068c2ecf20Sopenharmony_ci	/* Initialize controls. */
23078c2ecf20Sopenharmony_ci	if (uvc_ctrl_init_device(dev) < 0)
23088c2ecf20Sopenharmony_ci		goto error;
23098c2ecf20Sopenharmony_ci
23108c2ecf20Sopenharmony_ci	/* Scan the device for video chains. */
23118c2ecf20Sopenharmony_ci	if (uvc_scan_device(dev) < 0)
23128c2ecf20Sopenharmony_ci		goto error;
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_ci	/* Register video device nodes. */
23158c2ecf20Sopenharmony_ci	if (uvc_register_chains(dev) < 0)
23168c2ecf20Sopenharmony_ci		goto error;
23178c2ecf20Sopenharmony_ci
23188c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER
23198c2ecf20Sopenharmony_ci	/* Register the media device node */
23208c2ecf20Sopenharmony_ci	if (media_device_register(&dev->mdev) < 0)
23218c2ecf20Sopenharmony_ci		goto error;
23228c2ecf20Sopenharmony_ci#endif
23238c2ecf20Sopenharmony_ci	/* Save our data pointer in the interface data. */
23248c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, dev);
23258c2ecf20Sopenharmony_ci
23268c2ecf20Sopenharmony_ci	/* Initialize the interrupt URB. */
23278c2ecf20Sopenharmony_ci	if ((ret = uvc_status_init(dev)) < 0) {
23288c2ecf20Sopenharmony_ci		uvc_printk(KERN_INFO, "Unable to initialize the status "
23298c2ecf20Sopenharmony_ci			"endpoint (%d), status interrupt will not be "
23308c2ecf20Sopenharmony_ci			"supported.\n", ret);
23318c2ecf20Sopenharmony_ci	}
23328c2ecf20Sopenharmony_ci
23338c2ecf20Sopenharmony_ci	uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
23348c2ecf20Sopenharmony_ci	usb_enable_autosuspend(udev);
23358c2ecf20Sopenharmony_ci	return 0;
23368c2ecf20Sopenharmony_ci
23378c2ecf20Sopenharmony_cierror:
23388c2ecf20Sopenharmony_ci	uvc_unregister_video(dev);
23398c2ecf20Sopenharmony_ci	kref_put(&dev->ref, uvc_delete);
23408c2ecf20Sopenharmony_ci	return -ENODEV;
23418c2ecf20Sopenharmony_ci}
23428c2ecf20Sopenharmony_ci
23438c2ecf20Sopenharmony_cistatic void uvc_disconnect(struct usb_interface *intf)
23448c2ecf20Sopenharmony_ci{
23458c2ecf20Sopenharmony_ci	struct uvc_device *dev = usb_get_intfdata(intf);
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_ci	/* Set the USB interface data to NULL. This can be done outside the
23488c2ecf20Sopenharmony_ci	 * lock, as there's no other reader.
23498c2ecf20Sopenharmony_ci	 */
23508c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, NULL);
23518c2ecf20Sopenharmony_ci
23528c2ecf20Sopenharmony_ci	if (intf->cur_altsetting->desc.bInterfaceSubClass ==
23538c2ecf20Sopenharmony_ci	    UVC_SC_VIDEOSTREAMING)
23548c2ecf20Sopenharmony_ci		return;
23558c2ecf20Sopenharmony_ci
23568c2ecf20Sopenharmony_ci	uvc_unregister_video(dev);
23578c2ecf20Sopenharmony_ci	kref_put(&dev->ref, uvc_delete);
23588c2ecf20Sopenharmony_ci}
23598c2ecf20Sopenharmony_ci
23608c2ecf20Sopenharmony_cistatic int uvc_suspend(struct usb_interface *intf, pm_message_t message)
23618c2ecf20Sopenharmony_ci{
23628c2ecf20Sopenharmony_ci	struct uvc_device *dev = usb_get_intfdata(intf);
23638c2ecf20Sopenharmony_ci	struct uvc_streaming *stream;
23648c2ecf20Sopenharmony_ci
23658c2ecf20Sopenharmony_ci	uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n",
23668c2ecf20Sopenharmony_ci		intf->cur_altsetting->desc.bInterfaceNumber);
23678c2ecf20Sopenharmony_ci
23688c2ecf20Sopenharmony_ci	/* Controls are cached on the fly so they don't need to be saved. */
23698c2ecf20Sopenharmony_ci	if (intf->cur_altsetting->desc.bInterfaceSubClass ==
23708c2ecf20Sopenharmony_ci	    UVC_SC_VIDEOCONTROL) {
23718c2ecf20Sopenharmony_ci		mutex_lock(&dev->lock);
23728c2ecf20Sopenharmony_ci		if (dev->users)
23738c2ecf20Sopenharmony_ci			uvc_status_stop(dev);
23748c2ecf20Sopenharmony_ci		mutex_unlock(&dev->lock);
23758c2ecf20Sopenharmony_ci		return 0;
23768c2ecf20Sopenharmony_ci	}
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci	list_for_each_entry(stream, &dev->streams, list) {
23798c2ecf20Sopenharmony_ci		if (stream->intf == intf)
23808c2ecf20Sopenharmony_ci			return uvc_video_suspend(stream);
23818c2ecf20Sopenharmony_ci	}
23828c2ecf20Sopenharmony_ci
23838c2ecf20Sopenharmony_ci	uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface "
23848c2ecf20Sopenharmony_ci			"mismatch.\n");
23858c2ecf20Sopenharmony_ci	return -EINVAL;
23868c2ecf20Sopenharmony_ci}
23878c2ecf20Sopenharmony_ci
23888c2ecf20Sopenharmony_cistatic int __uvc_resume(struct usb_interface *intf, int reset)
23898c2ecf20Sopenharmony_ci{
23908c2ecf20Sopenharmony_ci	struct uvc_device *dev = usb_get_intfdata(intf);
23918c2ecf20Sopenharmony_ci	struct uvc_streaming *stream;
23928c2ecf20Sopenharmony_ci	int ret = 0;
23938c2ecf20Sopenharmony_ci
23948c2ecf20Sopenharmony_ci	uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n",
23958c2ecf20Sopenharmony_ci		intf->cur_altsetting->desc.bInterfaceNumber);
23968c2ecf20Sopenharmony_ci
23978c2ecf20Sopenharmony_ci	if (intf->cur_altsetting->desc.bInterfaceSubClass ==
23988c2ecf20Sopenharmony_ci	    UVC_SC_VIDEOCONTROL) {
23998c2ecf20Sopenharmony_ci		if (reset) {
24008c2ecf20Sopenharmony_ci			ret = uvc_ctrl_restore_values(dev);
24018c2ecf20Sopenharmony_ci			if (ret < 0)
24028c2ecf20Sopenharmony_ci				return ret;
24038c2ecf20Sopenharmony_ci		}
24048c2ecf20Sopenharmony_ci
24058c2ecf20Sopenharmony_ci		mutex_lock(&dev->lock);
24068c2ecf20Sopenharmony_ci		if (dev->users)
24078c2ecf20Sopenharmony_ci			ret = uvc_status_start(dev, GFP_NOIO);
24088c2ecf20Sopenharmony_ci		mutex_unlock(&dev->lock);
24098c2ecf20Sopenharmony_ci
24108c2ecf20Sopenharmony_ci		return ret;
24118c2ecf20Sopenharmony_ci	}
24128c2ecf20Sopenharmony_ci
24138c2ecf20Sopenharmony_ci	list_for_each_entry(stream, &dev->streams, list) {
24148c2ecf20Sopenharmony_ci		if (stream->intf == intf) {
24158c2ecf20Sopenharmony_ci			ret = uvc_video_resume(stream, reset);
24168c2ecf20Sopenharmony_ci			if (ret < 0)
24178c2ecf20Sopenharmony_ci				uvc_queue_streamoff(&stream->queue,
24188c2ecf20Sopenharmony_ci						    stream->queue.queue.type);
24198c2ecf20Sopenharmony_ci			return ret;
24208c2ecf20Sopenharmony_ci		}
24218c2ecf20Sopenharmony_ci	}
24228c2ecf20Sopenharmony_ci
24238c2ecf20Sopenharmony_ci	uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface "
24248c2ecf20Sopenharmony_ci			"mismatch.\n");
24258c2ecf20Sopenharmony_ci	return -EINVAL;
24268c2ecf20Sopenharmony_ci}
24278c2ecf20Sopenharmony_ci
24288c2ecf20Sopenharmony_cistatic int uvc_resume(struct usb_interface *intf)
24298c2ecf20Sopenharmony_ci{
24308c2ecf20Sopenharmony_ci	return __uvc_resume(intf, 0);
24318c2ecf20Sopenharmony_ci}
24328c2ecf20Sopenharmony_ci
24338c2ecf20Sopenharmony_cistatic int uvc_reset_resume(struct usb_interface *intf)
24348c2ecf20Sopenharmony_ci{
24358c2ecf20Sopenharmony_ci	return __uvc_resume(intf, 1);
24368c2ecf20Sopenharmony_ci}
24378c2ecf20Sopenharmony_ci
24388c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------
24398c2ecf20Sopenharmony_ci * Module parameters
24408c2ecf20Sopenharmony_ci */
24418c2ecf20Sopenharmony_ci
24428c2ecf20Sopenharmony_cistatic int uvc_clock_param_get(char *buffer, const struct kernel_param *kp)
24438c2ecf20Sopenharmony_ci{
24448c2ecf20Sopenharmony_ci	if (uvc_clock_param == CLOCK_MONOTONIC)
24458c2ecf20Sopenharmony_ci		return sprintf(buffer, "CLOCK_MONOTONIC");
24468c2ecf20Sopenharmony_ci	else
24478c2ecf20Sopenharmony_ci		return sprintf(buffer, "CLOCK_REALTIME");
24488c2ecf20Sopenharmony_ci}
24498c2ecf20Sopenharmony_ci
24508c2ecf20Sopenharmony_cistatic int uvc_clock_param_set(const char *val, const struct kernel_param *kp)
24518c2ecf20Sopenharmony_ci{
24528c2ecf20Sopenharmony_ci	if (strncasecmp(val, "clock_", strlen("clock_")) == 0)
24538c2ecf20Sopenharmony_ci		val += strlen("clock_");
24548c2ecf20Sopenharmony_ci
24558c2ecf20Sopenharmony_ci	if (strcasecmp(val, "monotonic") == 0)
24568c2ecf20Sopenharmony_ci		uvc_clock_param = CLOCK_MONOTONIC;
24578c2ecf20Sopenharmony_ci	else if (strcasecmp(val, "realtime") == 0)
24588c2ecf20Sopenharmony_ci		uvc_clock_param = CLOCK_REALTIME;
24598c2ecf20Sopenharmony_ci	else
24608c2ecf20Sopenharmony_ci		return -EINVAL;
24618c2ecf20Sopenharmony_ci
24628c2ecf20Sopenharmony_ci	return 0;
24638c2ecf20Sopenharmony_ci}
24648c2ecf20Sopenharmony_ci
24658c2ecf20Sopenharmony_cimodule_param_call(clock, uvc_clock_param_set, uvc_clock_param_get,
24668c2ecf20Sopenharmony_ci		  &uvc_clock_param, S_IRUGO|S_IWUSR);
24678c2ecf20Sopenharmony_ciMODULE_PARM_DESC(clock, "Video buffers timestamp clock");
24688c2ecf20Sopenharmony_cimodule_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, S_IRUGO|S_IWUSR);
24698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(hwtimestamps, "Use hardware timestamps");
24708c2ecf20Sopenharmony_cimodule_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR);
24718c2ecf20Sopenharmony_ciMODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
24728c2ecf20Sopenharmony_cimodule_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
24738c2ecf20Sopenharmony_ciMODULE_PARM_DESC(quirks, "Forced device quirks");
24748c2ecf20Sopenharmony_cimodule_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);
24758c2ecf20Sopenharmony_ciMODULE_PARM_DESC(trace, "Trace level bitmask");
24768c2ecf20Sopenharmony_cimodule_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR);
24778c2ecf20Sopenharmony_ciMODULE_PARM_DESC(timeout, "Streaming control requests timeout");
24788c2ecf20Sopenharmony_ci
24798c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------
24808c2ecf20Sopenharmony_ci * Driver initialization and cleanup
24818c2ecf20Sopenharmony_ci */
24828c2ecf20Sopenharmony_ci
24838c2ecf20Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_probe_minmax = {
24848c2ecf20Sopenharmony_ci	.quirks = UVC_QUIRK_PROBE_MINMAX,
24858c2ecf20Sopenharmony_ci};
24868c2ecf20Sopenharmony_ci
24878c2ecf20Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_fix_bandwidth = {
24888c2ecf20Sopenharmony_ci	.quirks = UVC_QUIRK_FIX_BANDWIDTH,
24898c2ecf20Sopenharmony_ci};
24908c2ecf20Sopenharmony_ci
24918c2ecf20Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_probe_def = {
24928c2ecf20Sopenharmony_ci	.quirks = UVC_QUIRK_PROBE_DEF,
24938c2ecf20Sopenharmony_ci};
24948c2ecf20Sopenharmony_ci
24958c2ecf20Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_stream_no_fid = {
24968c2ecf20Sopenharmony_ci	.quirks = UVC_QUIRK_STREAM_NO_FID,
24978c2ecf20Sopenharmony_ci};
24988c2ecf20Sopenharmony_ci
24998c2ecf20Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_force_y8 = {
25008c2ecf20Sopenharmony_ci	.quirks = UVC_QUIRK_FORCE_Y8,
25018c2ecf20Sopenharmony_ci};
25028c2ecf20Sopenharmony_ci
25038c2ecf20Sopenharmony_ci#define UVC_INFO_QUIRK(q) (kernel_ulong_t)&(struct uvc_device_info){.quirks = q}
25048c2ecf20Sopenharmony_ci#define UVC_INFO_META(m) (kernel_ulong_t)&(struct uvc_device_info) \
25058c2ecf20Sopenharmony_ci	{.meta_format = m}
25068c2ecf20Sopenharmony_ci
25078c2ecf20Sopenharmony_ci/*
25088c2ecf20Sopenharmony_ci * The Logitech cameras listed below have their interface class set to
25098c2ecf20Sopenharmony_ci * VENDOR_SPEC because they don't announce themselves as UVC devices, even
25108c2ecf20Sopenharmony_ci * though they are compliant.
25118c2ecf20Sopenharmony_ci */
25128c2ecf20Sopenharmony_cistatic const struct usb_device_id uvc_ids[] = {
25138c2ecf20Sopenharmony_ci	/* LogiLink Wireless Webcam */
25148c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
25158c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
25168c2ecf20Sopenharmony_ci	  .idVendor		= 0x0416,
25178c2ecf20Sopenharmony_ci	  .idProduct		= 0xa91a,
25188c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
25198c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
25208c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
25218c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
25228c2ecf20Sopenharmony_ci	/* Genius eFace 2025 */
25238c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
25248c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
25258c2ecf20Sopenharmony_ci	  .idVendor		= 0x0458,
25268c2ecf20Sopenharmony_ci	  .idProduct		= 0x706e,
25278c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
25288c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
25298c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
25308c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
25318c2ecf20Sopenharmony_ci	/* Microsoft Lifecam NX-6000 */
25328c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
25338c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
25348c2ecf20Sopenharmony_ci	  .idVendor		= 0x045e,
25358c2ecf20Sopenharmony_ci	  .idProduct		= 0x00f8,
25368c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
25378c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
25388c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
25398c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
25408c2ecf20Sopenharmony_ci	/* Microsoft Lifecam NX-3000 */
25418c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
25428c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
25438c2ecf20Sopenharmony_ci	  .idVendor		= 0x045e,
25448c2ecf20Sopenharmony_ci	  .idProduct		= 0x0721,
25458c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
25468c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
25478c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
25488c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_def },
25498c2ecf20Sopenharmony_ci	/* Microsoft Lifecam VX-7000 */
25508c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
25518c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
25528c2ecf20Sopenharmony_ci	  .idVendor		= 0x045e,
25538c2ecf20Sopenharmony_ci	  .idProduct		= 0x0723,
25548c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
25558c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
25568c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
25578c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
25588c2ecf20Sopenharmony_ci	/* Logitech, Webcam C910 */
25598c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
25608c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
25618c2ecf20Sopenharmony_ci	  .idVendor		= 0x046d,
25628c2ecf20Sopenharmony_ci	  .idProduct		= 0x0821,
25638c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
25648c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
25658c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
25668c2ecf20Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_WAKE_AUTOSUSPEND)},
25678c2ecf20Sopenharmony_ci	/* Logitech, Webcam B910 */
25688c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
25698c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
25708c2ecf20Sopenharmony_ci	  .idVendor		= 0x046d,
25718c2ecf20Sopenharmony_ci	  .idProduct		= 0x0823,
25728c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
25738c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
25748c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
25758c2ecf20Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_WAKE_AUTOSUSPEND)},
25768c2ecf20Sopenharmony_ci	/* Logitech Quickcam Fusion */
25778c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
25788c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
25798c2ecf20Sopenharmony_ci	  .idVendor		= 0x046d,
25808c2ecf20Sopenharmony_ci	  .idProduct		= 0x08c1,
25818c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
25828c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
25838c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0 },
25848c2ecf20Sopenharmony_ci	/* Logitech Quickcam Orbit MP */
25858c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
25868c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
25878c2ecf20Sopenharmony_ci	  .idVendor		= 0x046d,
25888c2ecf20Sopenharmony_ci	  .idProduct		= 0x08c2,
25898c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
25908c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
25918c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0 },
25928c2ecf20Sopenharmony_ci	/* Logitech Quickcam Pro for Notebook */
25938c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
25948c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
25958c2ecf20Sopenharmony_ci	  .idVendor		= 0x046d,
25968c2ecf20Sopenharmony_ci	  .idProduct		= 0x08c3,
25978c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
25988c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
25998c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0 },
26008c2ecf20Sopenharmony_ci	/* Logitech Quickcam Pro 5000 */
26018c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
26028c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
26038c2ecf20Sopenharmony_ci	  .idVendor		= 0x046d,
26048c2ecf20Sopenharmony_ci	  .idProduct		= 0x08c5,
26058c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
26068c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
26078c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0 },
26088c2ecf20Sopenharmony_ci	/* Logitech Quickcam OEM Dell Notebook */
26098c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
26108c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
26118c2ecf20Sopenharmony_ci	  .idVendor		= 0x046d,
26128c2ecf20Sopenharmony_ci	  .idProduct		= 0x08c6,
26138c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
26148c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
26158c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0 },
26168c2ecf20Sopenharmony_ci	/* Logitech Quickcam OEM Cisco VT Camera II */
26178c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
26188c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
26198c2ecf20Sopenharmony_ci	  .idVendor		= 0x046d,
26208c2ecf20Sopenharmony_ci	  .idProduct		= 0x08c7,
26218c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
26228c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
26238c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0 },
26248c2ecf20Sopenharmony_ci	/* Logitech HD Pro Webcam C920 */
26258c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
26268c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
26278c2ecf20Sopenharmony_ci	  .idVendor		= 0x046d,
26288c2ecf20Sopenharmony_ci	  .idProduct		= 0x082d,
26298c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
26308c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
26318c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
26328c2ecf20Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT) },
26338c2ecf20Sopenharmony_ci	/* Chicony CNF7129 (Asus EEE 100HE) */
26348c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
26358c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
26368c2ecf20Sopenharmony_ci	  .idVendor		= 0x04f2,
26378c2ecf20Sopenharmony_ci	  .idProduct		= 0xb071,
26388c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
26398c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
26408c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
26418c2ecf20Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_RESTRICT_FRAME_RATE) },
26428c2ecf20Sopenharmony_ci	/* Alcor Micro AU3820 (Future Boy PC USB Webcam) */
26438c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
26448c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
26458c2ecf20Sopenharmony_ci	  .idVendor		= 0x058f,
26468c2ecf20Sopenharmony_ci	  .idProduct		= 0x3820,
26478c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
26488c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
26498c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
26508c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
26518c2ecf20Sopenharmony_ci	/* Dell XPS m1530 */
26528c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
26538c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
26548c2ecf20Sopenharmony_ci	  .idVendor		= 0x05a9,
26558c2ecf20Sopenharmony_ci	  .idProduct		= 0x2640,
26568c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
26578c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
26588c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
26598c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_def },
26608c2ecf20Sopenharmony_ci	/* Dell SP2008WFP Monitor */
26618c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
26628c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
26638c2ecf20Sopenharmony_ci	  .idVendor		= 0x05a9,
26648c2ecf20Sopenharmony_ci	  .idProduct		= 0x2641,
26658c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
26668c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
26678c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
26688c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_def },
26698c2ecf20Sopenharmony_ci	/* Dell Alienware X51 */
26708c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
26718c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
26728c2ecf20Sopenharmony_ci	  .idVendor		= 0x05a9,
26738c2ecf20Sopenharmony_ci	  .idProduct		= 0x2643,
26748c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
26758c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
26768c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
26778c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_def },
26788c2ecf20Sopenharmony_ci	/* Dell Studio Hybrid 140g (OmniVision webcam) */
26798c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
26808c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
26818c2ecf20Sopenharmony_ci	  .idVendor		= 0x05a9,
26828c2ecf20Sopenharmony_ci	  .idProduct		= 0x264a,
26838c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
26848c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
26858c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
26868c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_def },
26878c2ecf20Sopenharmony_ci	/* Dell XPS M1330 (OmniVision OV7670 webcam) */
26888c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
26898c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
26908c2ecf20Sopenharmony_ci	  .idVendor		= 0x05a9,
26918c2ecf20Sopenharmony_ci	  .idProduct		= 0x7670,
26928c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
26938c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
26948c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
26958c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_def },
26968c2ecf20Sopenharmony_ci	/* Apple Built-In iSight */
26978c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
26988c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
26998c2ecf20Sopenharmony_ci	  .idVendor		= 0x05ac,
27008c2ecf20Sopenharmony_ci	  .idProduct		= 0x8501,
27018c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
27028c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
27038c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
27048c2ecf20Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX
27058c2ecf20Sopenharmony_ci					| UVC_QUIRK_BUILTIN_ISIGHT) },
27068c2ecf20Sopenharmony_ci	/* Apple Built-In iSight via iBridge */
27078c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
27088c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
27098c2ecf20Sopenharmony_ci	  .idVendor		= 0x05ac,
27108c2ecf20Sopenharmony_ci	  .idProduct		= 0x8600,
27118c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
27128c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
27138c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
27148c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_def },
27158c2ecf20Sopenharmony_ci	/* Foxlink ("HP Webcam" on HP Mini 5103) */
27168c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
27178c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
27188c2ecf20Sopenharmony_ci	  .idVendor		= 0x05c8,
27198c2ecf20Sopenharmony_ci	  .idProduct		= 0x0403,
27208c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
27218c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
27228c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
27238c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_fix_bandwidth },
27248c2ecf20Sopenharmony_ci	/* Genesys Logic USB 2.0 PC Camera */
27258c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
27268c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
27278c2ecf20Sopenharmony_ci	  .idVendor		= 0x05e3,
27288c2ecf20Sopenharmony_ci	  .idProduct		= 0x0505,
27298c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
27308c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
27318c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
27328c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
27338c2ecf20Sopenharmony_ci	/* Hercules Classic Silver */
27348c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
27358c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
27368c2ecf20Sopenharmony_ci	  .idVendor		= 0x06f8,
27378c2ecf20Sopenharmony_ci	  .idProduct		= 0x300c,
27388c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
27398c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
27408c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
27418c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_fix_bandwidth },
27428c2ecf20Sopenharmony_ci	/* ViMicro Vega */
27438c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
27448c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
27458c2ecf20Sopenharmony_ci	  .idVendor		= 0x0ac8,
27468c2ecf20Sopenharmony_ci	  .idProduct		= 0x332d,
27478c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
27488c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
27498c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
27508c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_fix_bandwidth },
27518c2ecf20Sopenharmony_ci	/* ViMicro - Minoru3D */
27528c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
27538c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
27548c2ecf20Sopenharmony_ci	  .idVendor		= 0x0ac8,
27558c2ecf20Sopenharmony_ci	  .idProduct		= 0x3410,
27568c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
27578c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
27588c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
27598c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_fix_bandwidth },
27608c2ecf20Sopenharmony_ci	/* ViMicro Venus - Minoru3D */
27618c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
27628c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
27638c2ecf20Sopenharmony_ci	  .idVendor		= 0x0ac8,
27648c2ecf20Sopenharmony_ci	  .idProduct		= 0x3420,
27658c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
27668c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
27678c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
27688c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_fix_bandwidth },
27698c2ecf20Sopenharmony_ci	/* Ophir Optronics - SPCAM 620U */
27708c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
27718c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
27728c2ecf20Sopenharmony_ci	  .idVendor		= 0x0bd3,
27738c2ecf20Sopenharmony_ci	  .idProduct		= 0x0555,
27748c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
27758c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
27768c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
27778c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
27788c2ecf20Sopenharmony_ci	/* MT6227 */
27798c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
27808c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
27818c2ecf20Sopenharmony_ci	  .idVendor		= 0x0e8d,
27828c2ecf20Sopenharmony_ci	  .idProduct		= 0x0004,
27838c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
27848c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
27858c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
27868c2ecf20Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX
27878c2ecf20Sopenharmony_ci					| UVC_QUIRK_PROBE_DEF) },
27888c2ecf20Sopenharmony_ci	/* IMC Networks (Medion Akoya) */
27898c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
27908c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
27918c2ecf20Sopenharmony_ci	  .idVendor		= 0x13d3,
27928c2ecf20Sopenharmony_ci	  .idProduct		= 0x5103,
27938c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
27948c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
27958c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
27968c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
27978c2ecf20Sopenharmony_ci	/* JMicron USB2.0 XGA WebCam */
27988c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
27998c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
28008c2ecf20Sopenharmony_ci	  .idVendor		= 0x152d,
28018c2ecf20Sopenharmony_ci	  .idProduct		= 0x0310,
28028c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
28038c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
28048c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
28058c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
28068c2ecf20Sopenharmony_ci	/* Syntek (HP Spartan) */
28078c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
28088c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
28098c2ecf20Sopenharmony_ci	  .idVendor		= 0x174f,
28108c2ecf20Sopenharmony_ci	  .idProduct		= 0x5212,
28118c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
28128c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
28138c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
28148c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
28158c2ecf20Sopenharmony_ci	/* Syntek (Samsung Q310) */
28168c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
28178c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
28188c2ecf20Sopenharmony_ci	  .idVendor		= 0x174f,
28198c2ecf20Sopenharmony_ci	  .idProduct		= 0x5931,
28208c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
28218c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
28228c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
28238c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
28248c2ecf20Sopenharmony_ci	/* Syntek (Packard Bell EasyNote MX52 */
28258c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
28268c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
28278c2ecf20Sopenharmony_ci	  .idVendor		= 0x174f,
28288c2ecf20Sopenharmony_ci	  .idProduct		= 0x8a12,
28298c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
28308c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
28318c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
28328c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
28338c2ecf20Sopenharmony_ci	/* Syntek (Asus F9SG) */
28348c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
28358c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
28368c2ecf20Sopenharmony_ci	  .idVendor		= 0x174f,
28378c2ecf20Sopenharmony_ci	  .idProduct		= 0x8a31,
28388c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
28398c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
28408c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
28418c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
28428c2ecf20Sopenharmony_ci	/* Syntek (Asus U3S) */
28438c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
28448c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
28458c2ecf20Sopenharmony_ci	  .idVendor		= 0x174f,
28468c2ecf20Sopenharmony_ci	  .idProduct		= 0x8a33,
28478c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
28488c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
28498c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
28508c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
28518c2ecf20Sopenharmony_ci	/* Syntek (JAOtech Smart Terminal) */
28528c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
28538c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
28548c2ecf20Sopenharmony_ci	  .idVendor		= 0x174f,
28558c2ecf20Sopenharmony_ci	  .idProduct		= 0x8a34,
28568c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
28578c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
28588c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
28598c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
28608c2ecf20Sopenharmony_ci	/* Miricle 307K */
28618c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
28628c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
28638c2ecf20Sopenharmony_ci	  .idVendor		= 0x17dc,
28648c2ecf20Sopenharmony_ci	  .idProduct		= 0x0202,
28658c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
28668c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
28678c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
28688c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
28698c2ecf20Sopenharmony_ci	/* Lenovo Thinkpad SL400/SL500 */
28708c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
28718c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
28728c2ecf20Sopenharmony_ci	  .idVendor		= 0x17ef,
28738c2ecf20Sopenharmony_ci	  .idProduct		= 0x480b,
28748c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
28758c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
28768c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
28778c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_stream_no_fid },
28788c2ecf20Sopenharmony_ci	/* Aveo Technology USB 2.0 Camera */
28798c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
28808c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
28818c2ecf20Sopenharmony_ci	  .idVendor		= 0x1871,
28828c2ecf20Sopenharmony_ci	  .idProduct		= 0x0306,
28838c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
28848c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
28858c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
28868c2ecf20Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX
28878c2ecf20Sopenharmony_ci					| UVC_QUIRK_PROBE_EXTRAFIELDS) },
28888c2ecf20Sopenharmony_ci	/* Aveo Technology USB 2.0 Camera (Tasco USB Microscope) */
28898c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
28908c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
28918c2ecf20Sopenharmony_ci	  .idVendor		= 0x1871,
28928c2ecf20Sopenharmony_ci	  .idProduct		= 0x0516,
28938c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
28948c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
28958c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0 },
28968c2ecf20Sopenharmony_ci	/* Ecamm Pico iMage */
28978c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
28988c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
28998c2ecf20Sopenharmony_ci	  .idVendor		= 0x18cd,
29008c2ecf20Sopenharmony_ci	  .idProduct		= 0xcafe,
29018c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
29028c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
29038c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
29048c2ecf20Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_PROBE_EXTRAFIELDS) },
29058c2ecf20Sopenharmony_ci	/* Manta MM-353 Plako */
29068c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
29078c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
29088c2ecf20Sopenharmony_ci	  .idVendor		= 0x18ec,
29098c2ecf20Sopenharmony_ci	  .idProduct		= 0x3188,
29108c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
29118c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
29128c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
29138c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
29148c2ecf20Sopenharmony_ci	/* FSC WebCam V30S */
29158c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
29168c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
29178c2ecf20Sopenharmony_ci	  .idVendor		= 0x18ec,
29188c2ecf20Sopenharmony_ci	  .idProduct		= 0x3288,
29198c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
29208c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
29218c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
29228c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
29238c2ecf20Sopenharmony_ci	/* Arkmicro unbranded */
29248c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
29258c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
29268c2ecf20Sopenharmony_ci	  .idVendor		= 0x18ec,
29278c2ecf20Sopenharmony_ci	  .idProduct		= 0x3290,
29288c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
29298c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
29308c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
29318c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_def },
29328c2ecf20Sopenharmony_ci	/* The Imaging Source USB CCD cameras */
29338c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
29348c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
29358c2ecf20Sopenharmony_ci	  .idVendor		= 0x199e,
29368c2ecf20Sopenharmony_ci	  .idProduct		= 0x8102,
29378c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
29388c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
29398c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0 },
29408c2ecf20Sopenharmony_ci	/* Bodelin ProScopeHR */
29418c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
29428c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_DEV_HI
29438c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
29448c2ecf20Sopenharmony_ci	  .idVendor		= 0x19ab,
29458c2ecf20Sopenharmony_ci	  .idProduct		= 0x1000,
29468c2ecf20Sopenharmony_ci	  .bcdDevice_hi		= 0x0126,
29478c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
29488c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
29498c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
29508c2ecf20Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_STATUS_INTERVAL) },
29518c2ecf20Sopenharmony_ci	/* MSI StarCam 370i */
29528c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
29538c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
29548c2ecf20Sopenharmony_ci	  .idVendor		= 0x1b3b,
29558c2ecf20Sopenharmony_ci	  .idProduct		= 0x2951,
29568c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
29578c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
29588c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
29598c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
29608c2ecf20Sopenharmony_ci	/* Generalplus Technology Inc. 808 Camera */
29618c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
29628c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
29638c2ecf20Sopenharmony_ci	  .idVendor		= 0x1b3f,
29648c2ecf20Sopenharmony_ci	  .idProduct		= 0x2002,
29658c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
29668c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
29678c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
29688c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_probe_minmax },
29698c2ecf20Sopenharmony_ci	/* SiGma Micro USB Web Camera */
29708c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
29718c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
29728c2ecf20Sopenharmony_ci	  .idVendor		= 0x1c4f,
29738c2ecf20Sopenharmony_ci	  .idProduct		= 0x3000,
29748c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
29758c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
29768c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
29778c2ecf20Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX
29788c2ecf20Sopenharmony_ci					| UVC_QUIRK_IGNORE_SELECTOR_UNIT) },
29798c2ecf20Sopenharmony_ci	/* Oculus VR Positional Tracker DK2 */
29808c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
29818c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
29828c2ecf20Sopenharmony_ci	  .idVendor		= 0x2833,
29838c2ecf20Sopenharmony_ci	  .idProduct		= 0x0201,
29848c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
29858c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
29868c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
29878c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_force_y8 },
29888c2ecf20Sopenharmony_ci	/* Oculus VR Rift Sensor */
29898c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
29908c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
29918c2ecf20Sopenharmony_ci	  .idVendor		= 0x2833,
29928c2ecf20Sopenharmony_ci	  .idProduct		= 0x0211,
29938c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
29948c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
29958c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
29968c2ecf20Sopenharmony_ci	  .driver_info		= (kernel_ulong_t)&uvc_quirk_force_y8 },
29978c2ecf20Sopenharmony_ci	/* GEO Semiconductor GC6500 */
29988c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
29998c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
30008c2ecf20Sopenharmony_ci	  .idVendor		= 0x29fe,
30018c2ecf20Sopenharmony_ci	  .idProduct		= 0x4d53,
30028c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
30038c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
30048c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
30058c2ecf20Sopenharmony_ci	  .driver_info		= UVC_INFO_QUIRK(UVC_QUIRK_FORCE_BPP) },
30068c2ecf20Sopenharmony_ci	/* Intel RealSense D4M */
30078c2ecf20Sopenharmony_ci	{ .match_flags		= USB_DEVICE_ID_MATCH_DEVICE
30088c2ecf20Sopenharmony_ci				| USB_DEVICE_ID_MATCH_INT_INFO,
30098c2ecf20Sopenharmony_ci	  .idVendor		= 0x8086,
30108c2ecf20Sopenharmony_ci	  .idProduct		= 0x0b03,
30118c2ecf20Sopenharmony_ci	  .bInterfaceClass	= USB_CLASS_VIDEO,
30128c2ecf20Sopenharmony_ci	  .bInterfaceSubClass	= 1,
30138c2ecf20Sopenharmony_ci	  .bInterfaceProtocol	= 0,
30148c2ecf20Sopenharmony_ci	  .driver_info		= UVC_INFO_META(V4L2_META_FMT_D4XX) },
30158c2ecf20Sopenharmony_ci	/* Generic USB Video Class */
30168c2ecf20Sopenharmony_ci	{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_UNDEFINED) },
30178c2ecf20Sopenharmony_ci	{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_15) },
30188c2ecf20Sopenharmony_ci	{}
30198c2ecf20Sopenharmony_ci};
30208c2ecf20Sopenharmony_ci
30218c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, uvc_ids);
30228c2ecf20Sopenharmony_ci
30238c2ecf20Sopenharmony_cistruct uvc_driver uvc_driver = {
30248c2ecf20Sopenharmony_ci	.driver = {
30258c2ecf20Sopenharmony_ci		.name		= "uvcvideo",
30268c2ecf20Sopenharmony_ci		.probe		= uvc_probe,
30278c2ecf20Sopenharmony_ci		.disconnect	= uvc_disconnect,
30288c2ecf20Sopenharmony_ci		.suspend	= uvc_suspend,
30298c2ecf20Sopenharmony_ci		.resume		= uvc_resume,
30308c2ecf20Sopenharmony_ci		.reset_resume	= uvc_reset_resume,
30318c2ecf20Sopenharmony_ci		.id_table	= uvc_ids,
30328c2ecf20Sopenharmony_ci		.supports_autosuspend = 1,
30338c2ecf20Sopenharmony_ci	},
30348c2ecf20Sopenharmony_ci};
30358c2ecf20Sopenharmony_ci
30368c2ecf20Sopenharmony_cistatic int __init uvc_init(void)
30378c2ecf20Sopenharmony_ci{
30388c2ecf20Sopenharmony_ci	int ret;
30398c2ecf20Sopenharmony_ci
30408c2ecf20Sopenharmony_ci	uvc_debugfs_init();
30418c2ecf20Sopenharmony_ci
30428c2ecf20Sopenharmony_ci	ret = usb_register(&uvc_driver.driver);
30438c2ecf20Sopenharmony_ci	if (ret < 0) {
30448c2ecf20Sopenharmony_ci		uvc_debugfs_cleanup();
30458c2ecf20Sopenharmony_ci		return ret;
30468c2ecf20Sopenharmony_ci	}
30478c2ecf20Sopenharmony_ci
30488c2ecf20Sopenharmony_ci	printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n");
30498c2ecf20Sopenharmony_ci	return 0;
30508c2ecf20Sopenharmony_ci}
30518c2ecf20Sopenharmony_ci
30528c2ecf20Sopenharmony_cistatic void __exit uvc_cleanup(void)
30538c2ecf20Sopenharmony_ci{
30548c2ecf20Sopenharmony_ci	usb_deregister(&uvc_driver.driver);
30558c2ecf20Sopenharmony_ci	uvc_debugfs_cleanup();
30568c2ecf20Sopenharmony_ci}
30578c2ecf20Sopenharmony_ci
30588c2ecf20Sopenharmony_cimodule_init(uvc_init);
30598c2ecf20Sopenharmony_cimodule_exit(uvc_cleanup);
30608c2ecf20Sopenharmony_ci
30618c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
30628c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
30638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
30648c2ecf20Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION);
30658c2ecf20Sopenharmony_ci
3066