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