12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 20c0d06caSMauro Carvalho Chehab /* 30c0d06caSMauro Carvalho Chehab * uvc_driver.c -- USB Video Class driver 40c0d06caSMauro Carvalho Chehab * 50c0d06caSMauro Carvalho Chehab * Copyright (C) 2005-2010 60c0d06caSMauro Carvalho Chehab * Laurent Pinchart (laurent.pinchart@ideasonboard.com) 70c0d06caSMauro Carvalho Chehab */ 80c0d06caSMauro Carvalho Chehab 90c0d06caSMauro Carvalho Chehab #include <linux/atomic.h> 102886477fSRicardo Ribalda #include <linux/gpio/consumer.h> 110c0d06caSMauro Carvalho Chehab #include <linux/kernel.h> 120c0d06caSMauro Carvalho Chehab #include <linux/list.h> 130c0d06caSMauro Carvalho Chehab #include <linux/module.h> 140c0d06caSMauro Carvalho Chehab #include <linux/slab.h> 150c0d06caSMauro Carvalho Chehab #include <linux/usb.h> 160c0d06caSMauro Carvalho Chehab #include <linux/videodev2.h> 170c0d06caSMauro Carvalho Chehab #include <linux/vmalloc.h> 180c0d06caSMauro Carvalho Chehab #include <linux/wait.h> 190c0d06caSMauro Carvalho Chehab #include <asm/unaligned.h> 200c0d06caSMauro Carvalho Chehab 210c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h> 2231a96f4cSLaurent Pinchart #include <media/v4l2-ioctl.h> 230c0d06caSMauro Carvalho Chehab 240c0d06caSMauro Carvalho Chehab #include "uvcvideo.h" 250c0d06caSMauro Carvalho Chehab 260c0d06caSMauro Carvalho Chehab #define DRIVER_AUTHOR "Laurent Pinchart " \ 270c0d06caSMauro Carvalho Chehab "<laurent.pinchart@ideasonboard.com>" 280c0d06caSMauro Carvalho Chehab #define DRIVER_DESC "USB Video Class driver" 290c0d06caSMauro Carvalho Chehab 300c0d06caSMauro Carvalho Chehab unsigned int uvc_clock_param = CLOCK_MONOTONIC; 315d0fd3c8SLaurent Pinchart unsigned int uvc_hw_timestamps_param; 320c0d06caSMauro Carvalho Chehab unsigned int uvc_no_drop_param; 330c0d06caSMauro Carvalho Chehab static unsigned int uvc_quirks_param = -1; 349e56380aSJoe Perches unsigned int uvc_dbg_param; 350c0d06caSMauro Carvalho Chehab unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; 360c0d06caSMauro Carvalho Chehab 370c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 380c0d06caSMauro Carvalho Chehab * Video formats 390c0d06caSMauro Carvalho Chehab */ 400c0d06caSMauro Carvalho Chehab 410c0d06caSMauro Carvalho Chehab static struct uvc_format_desc uvc_fmts[] = { 420c0d06caSMauro Carvalho Chehab { 430c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:2 (YUYV)", 440c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_YUY2, 450c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_YUYV, 460c0d06caSMauro Carvalho Chehab }, 470c0d06caSMauro Carvalho Chehab { 480c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:2 (YUYV)", 490c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_YUY2_ISIGHT, 500c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_YUYV, 510c0d06caSMauro Carvalho Chehab }, 520c0d06caSMauro Carvalho Chehab { 530c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:0 (NV12)", 540c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_NV12, 550c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_NV12, 560c0d06caSMauro Carvalho Chehab }, 570c0d06caSMauro Carvalho Chehab { 580c0d06caSMauro Carvalho Chehab .name = "MJPEG", 590c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_MJPEG, 600c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_MJPEG, 610c0d06caSMauro Carvalho Chehab }, 620c0d06caSMauro Carvalho Chehab { 630c0d06caSMauro Carvalho Chehab .name = "YVU 4:2:0 (YV12)", 640c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_YV12, 650c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_YVU420, 660c0d06caSMauro Carvalho Chehab }, 670c0d06caSMauro Carvalho Chehab { 680c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:0 (I420)", 690c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_I420, 700c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_YUV420, 710c0d06caSMauro Carvalho Chehab }, 720c0d06caSMauro Carvalho Chehab { 730c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:0 (M420)", 740c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_M420, 750c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_M420, 760c0d06caSMauro Carvalho Chehab }, 770c0d06caSMauro Carvalho Chehab { 780c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:2 (UYVY)", 790c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_UYVY, 800c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_UYVY, 810c0d06caSMauro Carvalho Chehab }, 820c0d06caSMauro Carvalho Chehab { 830c0d06caSMauro Carvalho Chehab .name = "Greyscale 8-bit (Y800)", 840c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y800, 850c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_GREY, 860c0d06caSMauro Carvalho Chehab }, 870c0d06caSMauro Carvalho Chehab { 880c0d06caSMauro Carvalho Chehab .name = "Greyscale 8-bit (Y8 )", 890c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y8, 900c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_GREY, 910c0d06caSMauro Carvalho Chehab }, 920c0d06caSMauro Carvalho Chehab { 93e96cdc9aSNicolas Dufresne .name = "Greyscale 8-bit (D3DFMT_L8)", 94e96cdc9aSNicolas Dufresne .guid = UVC_GUID_FORMAT_D3DFMT_L8, 95e96cdc9aSNicolas Dufresne .fcc = V4L2_PIX_FMT_GREY, 96e96cdc9aSNicolas Dufresne }, 97e96cdc9aSNicolas Dufresne { 98557a5c7fSLaurent Pinchart .name = "IR 8-bit (L8_IR)", 99557a5c7fSLaurent Pinchart .guid = UVC_GUID_FORMAT_KSMEDIA_L8_IR, 100557a5c7fSLaurent Pinchart .fcc = V4L2_PIX_FMT_GREY, 101557a5c7fSLaurent Pinchart }, 102557a5c7fSLaurent Pinchart { 1030c0d06caSMauro Carvalho Chehab .name = "Greyscale 10-bit (Y10 )", 1040c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y10, 1050c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_Y10, 1060c0d06caSMauro Carvalho Chehab }, 1070c0d06caSMauro Carvalho Chehab { 1080c0d06caSMauro Carvalho Chehab .name = "Greyscale 12-bit (Y12 )", 1090c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y12, 1100c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_Y12, 1110c0d06caSMauro Carvalho Chehab }, 1120c0d06caSMauro Carvalho Chehab { 1130c0d06caSMauro Carvalho Chehab .name = "Greyscale 16-bit (Y16 )", 1140c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y16, 1150c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_Y16, 1160c0d06caSMauro Carvalho Chehab }, 1170c0d06caSMauro Carvalho Chehab { 118e72ed08eSEdgar Thier .name = "BGGR Bayer (BY8 )", 1190c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_BY8, 1200c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_SBGGR8, 1210c0d06caSMauro Carvalho Chehab }, 1220c0d06caSMauro Carvalho Chehab { 123e72ed08eSEdgar Thier .name = "BGGR Bayer (BA81)", 124e72ed08eSEdgar Thier .guid = UVC_GUID_FORMAT_BA81, 125e72ed08eSEdgar Thier .fcc = V4L2_PIX_FMT_SBGGR8, 126e72ed08eSEdgar Thier }, 127e72ed08eSEdgar Thier { 128e72ed08eSEdgar Thier .name = "GBRG Bayer (GBRG)", 129e72ed08eSEdgar Thier .guid = UVC_GUID_FORMAT_GBRG, 130e72ed08eSEdgar Thier .fcc = V4L2_PIX_FMT_SGBRG8, 131e72ed08eSEdgar Thier }, 132e72ed08eSEdgar Thier { 133e72ed08eSEdgar Thier .name = "GRBG Bayer (GRBG)", 134e72ed08eSEdgar Thier .guid = UVC_GUID_FORMAT_GRBG, 135e72ed08eSEdgar Thier .fcc = V4L2_PIX_FMT_SGRBG8, 136e72ed08eSEdgar Thier }, 137e72ed08eSEdgar Thier { 138e72ed08eSEdgar Thier .name = "RGGB Bayer (RGGB)", 139e72ed08eSEdgar Thier .guid = UVC_GUID_FORMAT_RGGB, 140e72ed08eSEdgar Thier .fcc = V4L2_PIX_FMT_SRGGB8, 141e72ed08eSEdgar Thier }, 142e72ed08eSEdgar Thier { 1430c0d06caSMauro Carvalho Chehab .name = "RGB565", 1440c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_RGBP, 1450c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_RGB565, 1460c0d06caSMauro Carvalho Chehab }, 1470c0d06caSMauro Carvalho Chehab { 1481b8dc322SWilliam Manley .name = "BGR 8:8:8 (BGR3)", 1491b8dc322SWilliam Manley .guid = UVC_GUID_FORMAT_BGR3, 1501b8dc322SWilliam Manley .fcc = V4L2_PIX_FMT_BGR24, 1511b8dc322SWilliam Manley }, 1521b8dc322SWilliam Manley { 1530c0d06caSMauro Carvalho Chehab .name = "H.264", 1540c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_H264, 1550c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_H264, 1560c0d06caSMauro Carvalho Chehab }, 1575d8d8db8SAviv Greenberg { 158be938c70SJames_Lin .name = "H.265", 159be938c70SJames_Lin .guid = UVC_GUID_FORMAT_H265, 160be938c70SJames_Lin .fcc = V4L2_PIX_FMT_HEVC, 161be938c70SJames_Lin }, 162be938c70SJames_Lin { 1635d8d8db8SAviv Greenberg .name = "Greyscale 8 L/R (Y8I)", 1645d8d8db8SAviv Greenberg .guid = UVC_GUID_FORMAT_Y8I, 1655d8d8db8SAviv Greenberg .fcc = V4L2_PIX_FMT_Y8I, 1665d8d8db8SAviv Greenberg }, 1675d8d8db8SAviv Greenberg { 1685d8d8db8SAviv Greenberg .name = "Greyscale 12 L/R (Y12I)", 1695d8d8db8SAviv Greenberg .guid = UVC_GUID_FORMAT_Y12I, 1705d8d8db8SAviv Greenberg .fcc = V4L2_PIX_FMT_Y12I, 1715d8d8db8SAviv Greenberg }, 1725d8d8db8SAviv Greenberg { 1735d8d8db8SAviv Greenberg .name = "Depth data 16-bit (Z16)", 1745d8d8db8SAviv Greenberg .guid = UVC_GUID_FORMAT_Z16, 1755d8d8db8SAviv Greenberg .fcc = V4L2_PIX_FMT_Z16, 1765d8d8db8SAviv Greenberg }, 1775d8d8db8SAviv Greenberg { 1785d8d8db8SAviv Greenberg .name = "Bayer 10-bit (SRGGB10P)", 1795d8d8db8SAviv Greenberg .guid = UVC_GUID_FORMAT_RW10, 1805d8d8db8SAviv Greenberg .fcc = V4L2_PIX_FMT_SRGGB10P, 1815d8d8db8SAviv Greenberg }, 182eb165a20SEdgar Thier { 183eb165a20SEdgar Thier .name = "Bayer 16-bit (SBGGR16)", 184eb165a20SEdgar Thier .guid = UVC_GUID_FORMAT_BG16, 185eb165a20SEdgar Thier .fcc = V4L2_PIX_FMT_SBGGR16, 186eb165a20SEdgar Thier }, 187eb165a20SEdgar Thier { 188eb165a20SEdgar Thier .name = "Bayer 16-bit (SGBRG16)", 189eb165a20SEdgar Thier .guid = UVC_GUID_FORMAT_GB16, 190eb165a20SEdgar Thier .fcc = V4L2_PIX_FMT_SGBRG16, 191eb165a20SEdgar Thier }, 192eb165a20SEdgar Thier { 193eb165a20SEdgar Thier .name = "Bayer 16-bit (SRGGB16)", 194eb165a20SEdgar Thier .guid = UVC_GUID_FORMAT_RG16, 195eb165a20SEdgar Thier .fcc = V4L2_PIX_FMT_SRGGB16, 196eb165a20SEdgar Thier }, 197eb165a20SEdgar Thier { 198eb165a20SEdgar Thier .name = "Bayer 16-bit (SGRBG16)", 199eb165a20SEdgar Thier .guid = UVC_GUID_FORMAT_GR16, 200eb165a20SEdgar Thier .fcc = V4L2_PIX_FMT_SGRBG16, 201eb165a20SEdgar Thier }, 202c4a0968aSDaniel Patrick Johnson { 203c4a0968aSDaniel Patrick Johnson .name = "Depth data 16-bit (Z16)", 204c4a0968aSDaniel Patrick Johnson .guid = UVC_GUID_FORMAT_INVZ, 205c4a0968aSDaniel Patrick Johnson .fcc = V4L2_PIX_FMT_Z16, 206c4a0968aSDaniel Patrick Johnson }, 207c4a0968aSDaniel Patrick Johnson { 208c4a0968aSDaniel Patrick Johnson .name = "Greyscale 10-bit (Y10 )", 209c4a0968aSDaniel Patrick Johnson .guid = UVC_GUID_FORMAT_INVI, 210c4a0968aSDaniel Patrick Johnson .fcc = V4L2_PIX_FMT_Y10, 211c4a0968aSDaniel Patrick Johnson }, 212c4a0968aSDaniel Patrick Johnson { 213c4a0968aSDaniel Patrick Johnson .name = "IR:Depth 26-bit (INZI)", 214c4a0968aSDaniel Patrick Johnson .guid = UVC_GUID_FORMAT_INZI, 215c4a0968aSDaniel Patrick Johnson .fcc = V4L2_PIX_FMT_INZI, 216c4a0968aSDaniel Patrick Johnson }, 21738e9b928SSergey Dorodnicov { 21838e9b928SSergey Dorodnicov .name = "4-bit Depth Confidence (Packed)", 21938e9b928SSergey Dorodnicov .guid = UVC_GUID_FORMAT_CNF4, 22038e9b928SSergey Dorodnicov .fcc = V4L2_PIX_FMT_CNF4, 22138e9b928SSergey Dorodnicov }, 22240b222b5SDmitry Buzdyk { 22340b222b5SDmitry Buzdyk .name = "HEVC", 22440b222b5SDmitry Buzdyk .guid = UVC_GUID_FORMAT_HEVC, 22540b222b5SDmitry Buzdyk .fcc = V4L2_PIX_FMT_HEVC, 22640b222b5SDmitry Buzdyk }, 2270c0d06caSMauro Carvalho Chehab }; 2280c0d06caSMauro Carvalho Chehab 2290c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 2300c0d06caSMauro Carvalho Chehab * Utility functions 2310c0d06caSMauro Carvalho Chehab */ 2320c0d06caSMauro Carvalho Chehab 2330c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, 2342c6b222cSLaurent Pinchart u8 epaddr) 2350c0d06caSMauro Carvalho Chehab { 2360c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep; 2370c0d06caSMauro Carvalho Chehab unsigned int i; 2380c0d06caSMauro Carvalho Chehab 2390c0d06caSMauro Carvalho Chehab for (i = 0; i < alts->desc.bNumEndpoints; ++i) { 2400c0d06caSMauro Carvalho Chehab ep = &alts->endpoint[i]; 2410c0d06caSMauro Carvalho Chehab if (ep->desc.bEndpointAddress == epaddr) 2420c0d06caSMauro Carvalho Chehab return ep; 2430c0d06caSMauro Carvalho Chehab } 2440c0d06caSMauro Carvalho Chehab 2450c0d06caSMauro Carvalho Chehab return NULL; 2460c0d06caSMauro Carvalho Chehab } 2470c0d06caSMauro Carvalho Chehab 2482c6b222cSLaurent Pinchart static struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16]) 2490c0d06caSMauro Carvalho Chehab { 2500c0d06caSMauro Carvalho Chehab unsigned int len = ARRAY_SIZE(uvc_fmts); 2510c0d06caSMauro Carvalho Chehab unsigned int i; 2520c0d06caSMauro Carvalho Chehab 2530c0d06caSMauro Carvalho Chehab for (i = 0; i < len; ++i) { 2540c0d06caSMauro Carvalho Chehab if (memcmp(guid, uvc_fmts[i].guid, 16) == 0) 2550c0d06caSMauro Carvalho Chehab return &uvc_fmts[i]; 2560c0d06caSMauro Carvalho Chehab } 2570c0d06caSMauro Carvalho Chehab 2580c0d06caSMauro Carvalho Chehab return NULL; 2590c0d06caSMauro Carvalho Chehab } 2600c0d06caSMauro Carvalho Chehab 261ec2c23f6SAdam Goode static enum v4l2_colorspace uvc_colorspace(const u8 primaries) 2620c0d06caSMauro Carvalho Chehab { 263ec2c23f6SAdam Goode static const enum v4l2_colorspace colorprimaries[] = { 264e82822faSRicardo Ribalda V4L2_COLORSPACE_SRGB, /* Unspecified */ 2650c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SRGB, 2660c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_470_SYSTEM_M, 2670c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_470_SYSTEM_BG, 2680c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SMPTE170M, 2690c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SMPTE240M, 2700c0d06caSMauro Carvalho Chehab }; 2710c0d06caSMauro Carvalho Chehab 2720c0d06caSMauro Carvalho Chehab if (primaries < ARRAY_SIZE(colorprimaries)) 2730c0d06caSMauro Carvalho Chehab return colorprimaries[primaries]; 2740c0d06caSMauro Carvalho Chehab 275e82822faSRicardo Ribalda return V4L2_COLORSPACE_SRGB; /* Reserved */ 276ec2c23f6SAdam Goode } 277ec2c23f6SAdam Goode 278ec2c23f6SAdam Goode static enum v4l2_xfer_func uvc_xfer_func(const u8 transfer_characteristics) 279ec2c23f6SAdam Goode { 280ec2c23f6SAdam Goode /* 281ec2c23f6SAdam Goode * V4L2 does not currently have definitions for all possible values of 282ec2c23f6SAdam Goode * UVC transfer characteristics. If v4l2_xfer_func is extended with new 283ec2c23f6SAdam Goode * values, the mapping below should be updated. 284ec2c23f6SAdam Goode * 285ec2c23f6SAdam Goode * Substitutions are taken from the mapping given for 286ec2c23f6SAdam Goode * V4L2_XFER_FUNC_DEFAULT documented in videodev2.h. 287ec2c23f6SAdam Goode */ 288ec2c23f6SAdam Goode static const enum v4l2_xfer_func xfer_funcs[] = { 289ec2c23f6SAdam Goode V4L2_XFER_FUNC_DEFAULT, /* Unspecified */ 290ec2c23f6SAdam Goode V4L2_XFER_FUNC_709, 291ec2c23f6SAdam Goode V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 M */ 292ec2c23f6SAdam Goode V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 B, G */ 293ec2c23f6SAdam Goode V4L2_XFER_FUNC_709, /* Substitution for SMPTE 170M */ 294ec2c23f6SAdam Goode V4L2_XFER_FUNC_SMPTE240M, 295ec2c23f6SAdam Goode V4L2_XFER_FUNC_NONE, 296ec2c23f6SAdam Goode V4L2_XFER_FUNC_SRGB, 297ec2c23f6SAdam Goode }; 298ec2c23f6SAdam Goode 299ec2c23f6SAdam Goode if (transfer_characteristics < ARRAY_SIZE(xfer_funcs)) 300ec2c23f6SAdam Goode return xfer_funcs[transfer_characteristics]; 301ec2c23f6SAdam Goode 302ec2c23f6SAdam Goode return V4L2_XFER_FUNC_DEFAULT; /* Reserved */ 303ec2c23f6SAdam Goode } 304ec2c23f6SAdam Goode 305ec2c23f6SAdam Goode static enum v4l2_ycbcr_encoding uvc_ycbcr_enc(const u8 matrix_coefficients) 306ec2c23f6SAdam Goode { 307ec2c23f6SAdam Goode /* 308ec2c23f6SAdam Goode * V4L2 does not currently have definitions for all possible values of 309ec2c23f6SAdam Goode * UVC matrix coefficients. If v4l2_ycbcr_encoding is extended with new 310ec2c23f6SAdam Goode * values, the mapping below should be updated. 311ec2c23f6SAdam Goode * 312ec2c23f6SAdam Goode * Substitutions are taken from the mapping given for 313ec2c23f6SAdam Goode * V4L2_YCBCR_ENC_DEFAULT documented in videodev2.h. 314ec2c23f6SAdam Goode * 315ec2c23f6SAdam Goode * FCC is assumed to be close enough to 601. 316ec2c23f6SAdam Goode */ 317ec2c23f6SAdam Goode static const enum v4l2_ycbcr_encoding ycbcr_encs[] = { 318ec2c23f6SAdam Goode V4L2_YCBCR_ENC_DEFAULT, /* Unspecified */ 319ec2c23f6SAdam Goode V4L2_YCBCR_ENC_709, 320ec2c23f6SAdam Goode V4L2_YCBCR_ENC_601, /* Substitution for FCC */ 321ec2c23f6SAdam Goode V4L2_YCBCR_ENC_601, /* Substitution for BT.470-2 B, G */ 322ec2c23f6SAdam Goode V4L2_YCBCR_ENC_601, 323ec2c23f6SAdam Goode V4L2_YCBCR_ENC_SMPTE240M, 324ec2c23f6SAdam Goode }; 325ec2c23f6SAdam Goode 326ec2c23f6SAdam Goode if (matrix_coefficients < ARRAY_SIZE(ycbcr_encs)) 327ec2c23f6SAdam Goode return ycbcr_encs[matrix_coefficients]; 328ec2c23f6SAdam Goode 329ec2c23f6SAdam Goode return V4L2_YCBCR_ENC_DEFAULT; /* Reserved */ 3300c0d06caSMauro Carvalho Chehab } 3310c0d06caSMauro Carvalho Chehab 332699b9a86SLaurent Pinchart /* 333699b9a86SLaurent Pinchart * Simplify a fraction using a simple continued fraction decomposition. The 3340c0d06caSMauro Carvalho Chehab * idea here is to convert fractions such as 333333/10000000 to 1/30 using 3350c0d06caSMauro Carvalho Chehab * 32 bit arithmetic only. The algorithm is not perfect and relies upon two 3360c0d06caSMauro Carvalho Chehab * arbitrary parameters to remove non-significative terms from the simple 3370c0d06caSMauro Carvalho Chehab * continued fraction decomposition. Using 8 and 333 for n_terms and threshold 3380c0d06caSMauro Carvalho Chehab * respectively seems to give nice results. 3390c0d06caSMauro Carvalho Chehab */ 3401e304c47SLaurent Pinchart void uvc_simplify_fraction(u32 *numerator, u32 *denominator, 3410c0d06caSMauro Carvalho Chehab unsigned int n_terms, unsigned int threshold) 3420c0d06caSMauro Carvalho Chehab { 3431e304c47SLaurent Pinchart u32 *an; 3441e304c47SLaurent Pinchart u32 x, y, r; 3450c0d06caSMauro Carvalho Chehab unsigned int i, n; 3460c0d06caSMauro Carvalho Chehab 347f14d4988SLaurent Pinchart an = kmalloc_array(n_terms, sizeof(*an), GFP_KERNEL); 3480c0d06caSMauro Carvalho Chehab if (an == NULL) 3490c0d06caSMauro Carvalho Chehab return; 3500c0d06caSMauro Carvalho Chehab 351699b9a86SLaurent Pinchart /* 352699b9a86SLaurent Pinchart * Convert the fraction to a simple continued fraction. See 353ebd6bae3SLaurent Pinchart * https://en.wikipedia.org/wiki/Continued_fraction 3540c0d06caSMauro Carvalho Chehab * Stop if the current term is bigger than or equal to the given 3550c0d06caSMauro Carvalho Chehab * threshold. 3560c0d06caSMauro Carvalho Chehab */ 3570c0d06caSMauro Carvalho Chehab x = *numerator; 3580c0d06caSMauro Carvalho Chehab y = *denominator; 3590c0d06caSMauro Carvalho Chehab 3600c0d06caSMauro Carvalho Chehab for (n = 0; n < n_terms && y != 0; ++n) { 3610c0d06caSMauro Carvalho Chehab an[n] = x / y; 3620c0d06caSMauro Carvalho Chehab if (an[n] >= threshold) { 3630c0d06caSMauro Carvalho Chehab if (n < 2) 3640c0d06caSMauro Carvalho Chehab n++; 3650c0d06caSMauro Carvalho Chehab break; 3660c0d06caSMauro Carvalho Chehab } 3670c0d06caSMauro Carvalho Chehab 3680c0d06caSMauro Carvalho Chehab r = x - an[n] * y; 3690c0d06caSMauro Carvalho Chehab x = y; 3700c0d06caSMauro Carvalho Chehab y = r; 3710c0d06caSMauro Carvalho Chehab } 3720c0d06caSMauro Carvalho Chehab 3730c0d06caSMauro Carvalho Chehab /* Expand the simple continued fraction back to an integer fraction. */ 3740c0d06caSMauro Carvalho Chehab x = 0; 3750c0d06caSMauro Carvalho Chehab y = 1; 3760c0d06caSMauro Carvalho Chehab 3770c0d06caSMauro Carvalho Chehab for (i = n; i > 0; --i) { 3780c0d06caSMauro Carvalho Chehab r = y; 3790c0d06caSMauro Carvalho Chehab y = an[i-1] * y + x; 3800c0d06caSMauro Carvalho Chehab x = r; 3810c0d06caSMauro Carvalho Chehab } 3820c0d06caSMauro Carvalho Chehab 3830c0d06caSMauro Carvalho Chehab *numerator = y; 3840c0d06caSMauro Carvalho Chehab *denominator = x; 3850c0d06caSMauro Carvalho Chehab kfree(an); 3860c0d06caSMauro Carvalho Chehab } 3870c0d06caSMauro Carvalho Chehab 388699b9a86SLaurent Pinchart /* 389699b9a86SLaurent Pinchart * Convert a fraction to a frame interval in 100ns multiples. The idea here is 3900c0d06caSMauro Carvalho Chehab * to compute numerator / denominator * 10000000 using 32 bit fixed point 3910c0d06caSMauro Carvalho Chehab * arithmetic only. 3920c0d06caSMauro Carvalho Chehab */ 3931e304c47SLaurent Pinchart u32 uvc_fraction_to_interval(u32 numerator, u32 denominator) 3940c0d06caSMauro Carvalho Chehab { 3951e304c47SLaurent Pinchart u32 multiplier; 3960c0d06caSMauro Carvalho Chehab 3970c0d06caSMauro Carvalho Chehab /* Saturate the result if the operation would overflow. */ 3980c0d06caSMauro Carvalho Chehab if (denominator == 0 || 3991e304c47SLaurent Pinchart numerator/denominator >= ((u32)-1)/10000000) 4001e304c47SLaurent Pinchart return (u32)-1; 4010c0d06caSMauro Carvalho Chehab 402699b9a86SLaurent Pinchart /* 403699b9a86SLaurent Pinchart * Divide both the denominator and the multiplier by two until 4040c0d06caSMauro Carvalho Chehab * numerator * multiplier doesn't overflow. If anyone knows a better 4050c0d06caSMauro Carvalho Chehab * algorithm please let me know. 4060c0d06caSMauro Carvalho Chehab */ 4070c0d06caSMauro Carvalho Chehab multiplier = 10000000; 4081e304c47SLaurent Pinchart while (numerator > ((u32)-1)/multiplier) { 4090c0d06caSMauro Carvalho Chehab multiplier /= 2; 4100c0d06caSMauro Carvalho Chehab denominator /= 2; 4110c0d06caSMauro Carvalho Chehab } 4120c0d06caSMauro Carvalho Chehab 4130c0d06caSMauro Carvalho Chehab return denominator ? numerator * multiplier / denominator : 0; 4140c0d06caSMauro Carvalho Chehab } 4150c0d06caSMauro Carvalho Chehab 4160c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 4170c0d06caSMauro Carvalho Chehab * Terminal and unit management 4180c0d06caSMauro Carvalho Chehab */ 4190c0d06caSMauro Carvalho Chehab 4200c0d06caSMauro Carvalho Chehab struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id) 4210c0d06caSMauro Carvalho Chehab { 4220c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 4230c0d06caSMauro Carvalho Chehab 4240c0d06caSMauro Carvalho Chehab list_for_each_entry(entity, &dev->entities, list) { 4250c0d06caSMauro Carvalho Chehab if (entity->id == id) 4260c0d06caSMauro Carvalho Chehab return entity; 4270c0d06caSMauro Carvalho Chehab } 4280c0d06caSMauro Carvalho Chehab 4290c0d06caSMauro Carvalho Chehab return NULL; 4300c0d06caSMauro Carvalho Chehab } 4310c0d06caSMauro Carvalho Chehab 4320c0d06caSMauro Carvalho Chehab static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, 4330c0d06caSMauro Carvalho Chehab int id, struct uvc_entity *entity) 4340c0d06caSMauro Carvalho Chehab { 4350c0d06caSMauro Carvalho Chehab unsigned int i; 4360c0d06caSMauro Carvalho Chehab 4370c0d06caSMauro Carvalho Chehab if (entity == NULL) 4380c0d06caSMauro Carvalho Chehab entity = list_entry(&dev->entities, struct uvc_entity, list); 4390c0d06caSMauro Carvalho Chehab 4400c0d06caSMauro Carvalho Chehab list_for_each_entry_continue(entity, &dev->entities, list) { 4410c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->bNrInPins; ++i) 4420c0d06caSMauro Carvalho Chehab if (entity->baSourceID[i] == id) 4430c0d06caSMauro Carvalho Chehab return entity; 4440c0d06caSMauro Carvalho Chehab } 4450c0d06caSMauro Carvalho Chehab 4460c0d06caSMauro Carvalho Chehab return NULL; 4470c0d06caSMauro Carvalho Chehab } 4480c0d06caSMauro Carvalho Chehab 4490c0d06caSMauro Carvalho Chehab static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id) 4500c0d06caSMauro Carvalho Chehab { 4510c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 4520c0d06caSMauro Carvalho Chehab 4530c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 4540c0d06caSMauro Carvalho Chehab if (stream->header.bTerminalLink == id) 4550c0d06caSMauro Carvalho Chehab return stream; 4560c0d06caSMauro Carvalho Chehab } 4570c0d06caSMauro Carvalho Chehab 4580c0d06caSMauro Carvalho Chehab return NULL; 4590c0d06caSMauro Carvalho Chehab } 4600c0d06caSMauro Carvalho Chehab 4610c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 462ece41454SKieran Bingham * Streaming Object Management 463ece41454SKieran Bingham */ 464ece41454SKieran Bingham 465ece41454SKieran Bingham static void uvc_stream_delete(struct uvc_streaming *stream) 466ece41454SKieran Bingham { 467b012186aSKieran Bingham if (stream->async_wq) 468b012186aSKieran Bingham destroy_workqueue(stream->async_wq); 469b012186aSKieran Bingham 470ece41454SKieran Bingham mutex_destroy(&stream->mutex); 471ece41454SKieran Bingham 472ece41454SKieran Bingham usb_put_intf(stream->intf); 473ece41454SKieran Bingham 474ece41454SKieran Bingham kfree(stream->format); 475ece41454SKieran Bingham kfree(stream->header.bmaControls); 476ece41454SKieran Bingham kfree(stream); 477ece41454SKieran Bingham } 478ece41454SKieran Bingham 479ece41454SKieran Bingham static struct uvc_streaming *uvc_stream_new(struct uvc_device *dev, 480ece41454SKieran Bingham struct usb_interface *intf) 481ece41454SKieran Bingham { 482ece41454SKieran Bingham struct uvc_streaming *stream; 483ece41454SKieran Bingham 484ece41454SKieran Bingham stream = kzalloc(sizeof(*stream), GFP_KERNEL); 485ece41454SKieran Bingham if (stream == NULL) 486ece41454SKieran Bingham return NULL; 487ece41454SKieran Bingham 488ece41454SKieran Bingham mutex_init(&stream->mutex); 489ece41454SKieran Bingham 490ece41454SKieran Bingham stream->dev = dev; 491ece41454SKieran Bingham stream->intf = usb_get_intf(intf); 492ece41454SKieran Bingham stream->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; 493ece41454SKieran Bingham 494b012186aSKieran Bingham /* Allocate a stream specific work queue for asynchronous tasks. */ 495b012186aSKieran Bingham stream->async_wq = alloc_workqueue("uvcvideo", WQ_UNBOUND | WQ_HIGHPRI, 496b012186aSKieran Bingham 0); 497b012186aSKieran Bingham if (!stream->async_wq) { 498b012186aSKieran Bingham uvc_stream_delete(stream); 499b012186aSKieran Bingham return NULL; 500b012186aSKieran Bingham } 501b012186aSKieran Bingham 502ece41454SKieran Bingham return stream; 503ece41454SKieran Bingham } 504ece41454SKieran Bingham 505ece41454SKieran Bingham /* ------------------------------------------------------------------------ 5060c0d06caSMauro Carvalho Chehab * Descriptors parsing 5070c0d06caSMauro Carvalho Chehab */ 5080c0d06caSMauro Carvalho Chehab 5090c0d06caSMauro Carvalho Chehab static int uvc_parse_format(struct uvc_device *dev, 5100c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming, struct uvc_format *format, 5112c6b222cSLaurent Pinchart u32 **intervals, unsigned char *buffer, int buflen) 5120c0d06caSMauro Carvalho Chehab { 5130c0d06caSMauro Carvalho Chehab struct usb_interface *intf = streaming->intf; 5140c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = intf->cur_altsetting; 5150c0d06caSMauro Carvalho Chehab struct uvc_format_desc *fmtdesc; 5160c0d06caSMauro Carvalho Chehab struct uvc_frame *frame; 5170c0d06caSMauro Carvalho Chehab const unsigned char *start = buffer; 518e1b78a33SPhilipp Zabel unsigned int width_multiplier = 1; 5190c0d06caSMauro Carvalho Chehab unsigned int interval; 5200c0d06caSMauro Carvalho Chehab unsigned int i, n; 5212c6b222cSLaurent Pinchart u8 ftype; 5220c0d06caSMauro Carvalho Chehab 5230c0d06caSMauro Carvalho Chehab format->type = buffer[2]; 5240c0d06caSMauro Carvalho Chehab format->index = buffer[3]; 5250c0d06caSMauro Carvalho Chehab 5260c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 5270c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 5280c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 5290c0d06caSMauro Carvalho Chehab n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28; 5300c0d06caSMauro Carvalho Chehab if (buflen < n) { 5319e56380aSJoe Perches uvc_dbg(dev, DESCR, 532ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d FORMAT error\n", 5330c0d06caSMauro Carvalho Chehab dev->udev->devnum, 5340c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 5350c0d06caSMauro Carvalho Chehab return -EINVAL; 5360c0d06caSMauro Carvalho Chehab } 5370c0d06caSMauro Carvalho Chehab 5380c0d06caSMauro Carvalho Chehab /* Find the format descriptor from its GUID. */ 5390c0d06caSMauro Carvalho Chehab fmtdesc = uvc_format_by_guid(&buffer[5]); 5400c0d06caSMauro Carvalho Chehab 5410c0d06caSMauro Carvalho Chehab if (fmtdesc != NULL) { 542c0decac1SMauro Carvalho Chehab strscpy(format->name, fmtdesc->name, 543f14d4988SLaurent Pinchart sizeof(format->name)); 5440c0d06caSMauro Carvalho Chehab format->fcc = fmtdesc->fcc; 5450c0d06caSMauro Carvalho Chehab } else { 54669df0954SRicardo Ribalda dev_info(&streaming->intf->dev, 54769df0954SRicardo Ribalda "Unknown video format %pUl\n", &buffer[5]); 5480c0d06caSMauro Carvalho Chehab snprintf(format->name, sizeof(format->name), "%pUl\n", 5490c0d06caSMauro Carvalho Chehab &buffer[5]); 5500c0d06caSMauro Carvalho Chehab format->fcc = 0; 5510c0d06caSMauro Carvalho Chehab } 5520c0d06caSMauro Carvalho Chehab 5530c0d06caSMauro Carvalho Chehab format->bpp = buffer[21]; 554e1b78a33SPhilipp Zabel 555699b9a86SLaurent Pinchart /* 556699b9a86SLaurent Pinchart * Some devices report a format that doesn't match what they 557e1b78a33SPhilipp Zabel * really send. 558e1b78a33SPhilipp Zabel */ 559e1b78a33SPhilipp Zabel if (dev->quirks & UVC_QUIRK_FORCE_Y8) { 560e1b78a33SPhilipp Zabel if (format->fcc == V4L2_PIX_FMT_YUYV) { 561c0decac1SMauro Carvalho Chehab strscpy(format->name, "Greyscale 8-bit (Y8 )", 562e1b78a33SPhilipp Zabel sizeof(format->name)); 563e1b78a33SPhilipp Zabel format->fcc = V4L2_PIX_FMT_GREY; 564e1b78a33SPhilipp Zabel format->bpp = 8; 565e1b78a33SPhilipp Zabel width_multiplier = 2; 566e1b78a33SPhilipp Zabel } 567e1b78a33SPhilipp Zabel } 568e1b78a33SPhilipp Zabel 5691dd2e8f9SSergey Zakharchenko /* Some devices report bpp that doesn't match the format. */ 5701dd2e8f9SSergey Zakharchenko if (dev->quirks & UVC_QUIRK_FORCE_BPP) { 5711dd2e8f9SSergey Zakharchenko const struct v4l2_format_info *info = 5721dd2e8f9SSergey Zakharchenko v4l2_format_info(format->fcc); 5731dd2e8f9SSergey Zakharchenko 5741dd2e8f9SSergey Zakharchenko if (info) { 5751dd2e8f9SSergey Zakharchenko unsigned int div = info->hdiv * info->vdiv; 5761dd2e8f9SSergey Zakharchenko 5771dd2e8f9SSergey Zakharchenko n = info->bpp[0] * div; 5781dd2e8f9SSergey Zakharchenko for (i = 1; i < info->comp_planes; i++) 5791dd2e8f9SSergey Zakharchenko n += info->bpp[i]; 5801dd2e8f9SSergey Zakharchenko 5811dd2e8f9SSergey Zakharchenko format->bpp = DIV_ROUND_UP(8 * n, div); 5821dd2e8f9SSergey Zakharchenko } 5831dd2e8f9SSergey Zakharchenko } 5841dd2e8f9SSergey Zakharchenko 5850c0d06caSMauro Carvalho Chehab if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) { 5860c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_UNCOMPRESSED; 5870c0d06caSMauro Carvalho Chehab } else { 5880c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_FRAME_BASED; 5890c0d06caSMauro Carvalho Chehab if (buffer[27]) 5900c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED; 5910c0d06caSMauro Carvalho Chehab } 5920c0d06caSMauro Carvalho Chehab break; 5930c0d06caSMauro Carvalho Chehab 5940c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 5950c0d06caSMauro Carvalho Chehab if (buflen < 11) { 5969e56380aSJoe Perches uvc_dbg(dev, DESCR, 597ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d FORMAT error\n", 5980c0d06caSMauro Carvalho Chehab dev->udev->devnum, 5990c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 6000c0d06caSMauro Carvalho Chehab return -EINVAL; 6010c0d06caSMauro Carvalho Chehab } 6020c0d06caSMauro Carvalho Chehab 603c0decac1SMauro Carvalho Chehab strscpy(format->name, "MJPEG", sizeof(format->name)); 6040c0d06caSMauro Carvalho Chehab format->fcc = V4L2_PIX_FMT_MJPEG; 6050c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED; 6060c0d06caSMauro Carvalho Chehab format->bpp = 0; 6070c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_MJPEG; 6080c0d06caSMauro Carvalho Chehab break; 6090c0d06caSMauro Carvalho Chehab 6100c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 6110c0d06caSMauro Carvalho Chehab if (buflen < 9) { 6129e56380aSJoe Perches uvc_dbg(dev, DESCR, 613ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d FORMAT error\n", 6140c0d06caSMauro Carvalho Chehab dev->udev->devnum, 6150c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 6160c0d06caSMauro Carvalho Chehab return -EINVAL; 6170c0d06caSMauro Carvalho Chehab } 6180c0d06caSMauro Carvalho Chehab 6190c0d06caSMauro Carvalho Chehab switch (buffer[8] & 0x7f) { 6200c0d06caSMauro Carvalho Chehab case 0: 621c0decac1SMauro Carvalho Chehab strscpy(format->name, "SD-DV", sizeof(format->name)); 6220c0d06caSMauro Carvalho Chehab break; 6230c0d06caSMauro Carvalho Chehab case 1: 624c0decac1SMauro Carvalho Chehab strscpy(format->name, "SDL-DV", sizeof(format->name)); 6250c0d06caSMauro Carvalho Chehab break; 6260c0d06caSMauro Carvalho Chehab case 2: 627c0decac1SMauro Carvalho Chehab strscpy(format->name, "HD-DV", sizeof(format->name)); 6280c0d06caSMauro Carvalho Chehab break; 6290c0d06caSMauro Carvalho Chehab default: 6309e56380aSJoe Perches uvc_dbg(dev, DESCR, 631ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d: unknown DV format %u\n", 6320c0d06caSMauro Carvalho Chehab dev->udev->devnum, 6330c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, buffer[8]); 6340c0d06caSMauro Carvalho Chehab return -EINVAL; 6350c0d06caSMauro Carvalho Chehab } 6360c0d06caSMauro Carvalho Chehab 6370c0d06caSMauro Carvalho Chehab strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz", 638f14d4988SLaurent Pinchart sizeof(format->name)); 6390c0d06caSMauro Carvalho Chehab 6400c0d06caSMauro Carvalho Chehab format->fcc = V4L2_PIX_FMT_DV; 6410c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM; 6420c0d06caSMauro Carvalho Chehab format->bpp = 0; 6430c0d06caSMauro Carvalho Chehab ftype = 0; 6440c0d06caSMauro Carvalho Chehab 6450c0d06caSMauro Carvalho Chehab /* Create a dummy frame descriptor. */ 6460c0d06caSMauro Carvalho Chehab frame = &format->frame[0]; 647f14d4988SLaurent Pinchart memset(&format->frame[0], 0, sizeof(format->frame[0])); 6480c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = 1; 6490c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 1; 6500c0d06caSMauro Carvalho Chehab frame->dwFrameInterval = *intervals; 6510c0d06caSMauro Carvalho Chehab *(*intervals)++ = 1; 6520c0d06caSMauro Carvalho Chehab format->nframes = 1; 6530c0d06caSMauro Carvalho Chehab break; 6540c0d06caSMauro Carvalho Chehab 6550c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MPEG2TS: 6560c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_STREAM_BASED: 6570c0d06caSMauro Carvalho Chehab /* Not supported yet. */ 6580c0d06caSMauro Carvalho Chehab default: 6599e56380aSJoe Perches uvc_dbg(dev, DESCR, 660ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d unsupported format %u\n", 6610c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber, 6620c0d06caSMauro Carvalho Chehab buffer[2]); 6630c0d06caSMauro Carvalho Chehab return -EINVAL; 6640c0d06caSMauro Carvalho Chehab } 6650c0d06caSMauro Carvalho Chehab 6669e56380aSJoe Perches uvc_dbg(dev, DESCR, "Found format %s\n", format->name); 6670c0d06caSMauro Carvalho Chehab 6680c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 6690c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 6700c0d06caSMauro Carvalho Chehab 671699b9a86SLaurent Pinchart /* 672699b9a86SLaurent Pinchart * Parse the frame descriptors. Only uncompressed, MJPEG and frame 6730c0d06caSMauro Carvalho Chehab * based formats have frame descriptors. 6740c0d06caSMauro Carvalho Chehab */ 6750c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 6760c0d06caSMauro Carvalho Chehab buffer[2] == ftype) { 6770c0d06caSMauro Carvalho Chehab frame = &format->frame[format->nframes]; 6780c0d06caSMauro Carvalho Chehab if (ftype != UVC_VS_FRAME_FRAME_BASED) 6790c0d06caSMauro Carvalho Chehab n = buflen > 25 ? buffer[25] : 0; 6800c0d06caSMauro Carvalho Chehab else 6810c0d06caSMauro Carvalho Chehab n = buflen > 21 ? buffer[21] : 0; 6820c0d06caSMauro Carvalho Chehab 6830c0d06caSMauro Carvalho Chehab n = n ? n : 3; 6840c0d06caSMauro Carvalho Chehab 6850c0d06caSMauro Carvalho Chehab if (buflen < 26 + 4*n) { 6869e56380aSJoe Perches uvc_dbg(dev, DESCR, 687ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d FRAME error\n", 688ed4c5fa4SRicardo Ribalda dev->udev->devnum, 6890c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 6900c0d06caSMauro Carvalho Chehab return -EINVAL; 6910c0d06caSMauro Carvalho Chehab } 6920c0d06caSMauro Carvalho Chehab 6930c0d06caSMauro Carvalho Chehab frame->bFrameIndex = buffer[3]; 6940c0d06caSMauro Carvalho Chehab frame->bmCapabilities = buffer[4]; 695e1b78a33SPhilipp Zabel frame->wWidth = get_unaligned_le16(&buffer[5]) 696e1b78a33SPhilipp Zabel * width_multiplier; 6970c0d06caSMauro Carvalho Chehab frame->wHeight = get_unaligned_le16(&buffer[7]); 6980c0d06caSMauro Carvalho Chehab frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); 6990c0d06caSMauro Carvalho Chehab frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); 7000c0d06caSMauro Carvalho Chehab if (ftype != UVC_VS_FRAME_FRAME_BASED) { 7010c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = 7020c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[17]); 7030c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 7040c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[21]); 7050c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = buffer[25]; 7060c0d06caSMauro Carvalho Chehab } else { 7070c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = 0; 7080c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 7090c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[17]); 7100c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = buffer[21]; 7110c0d06caSMauro Carvalho Chehab } 7120c0d06caSMauro Carvalho Chehab frame->dwFrameInterval = *intervals; 7130c0d06caSMauro Carvalho Chehab 714699b9a86SLaurent Pinchart /* 715699b9a86SLaurent Pinchart * Several UVC chipsets screw up dwMaxVideoFrameBufferSize 7160c0d06caSMauro Carvalho Chehab * completely. Observed behaviours range from setting the 7170c0d06caSMauro Carvalho Chehab * value to 1.1x the actual frame size to hardwiring the 7180c0d06caSMauro Carvalho Chehab * 16 low bits to 0. This results in a higher than necessary 7190c0d06caSMauro Carvalho Chehab * memory usage as well as a wrong image size information. For 7200c0d06caSMauro Carvalho Chehab * uncompressed formats this can be fixed by computing the 7210c0d06caSMauro Carvalho Chehab * value from the frame size. 7220c0d06caSMauro Carvalho Chehab */ 7230c0d06caSMauro Carvalho Chehab if (!(format->flags & UVC_FMT_FLAG_COMPRESSED)) 7240c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = format->bpp 7250c0d06caSMauro Carvalho Chehab * frame->wWidth * frame->wHeight / 8; 7260c0d06caSMauro Carvalho Chehab 727699b9a86SLaurent Pinchart /* 728699b9a86SLaurent Pinchart * Some bogus devices report dwMinFrameInterval equal to 7290c0d06caSMauro Carvalho Chehab * dwMaxFrameInterval and have dwFrameIntervalStep set to 7300c0d06caSMauro Carvalho Chehab * zero. Setting all null intervals to 1 fixes the problem and 7310c0d06caSMauro Carvalho Chehab * some other divisions by zero that could happen. 7320c0d06caSMauro Carvalho Chehab */ 7330c0d06caSMauro Carvalho Chehab for (i = 0; i < n; ++i) { 7340c0d06caSMauro Carvalho Chehab interval = get_unaligned_le32(&buffer[26+4*i]); 7350c0d06caSMauro Carvalho Chehab *(*intervals)++ = interval ? interval : 1; 7360c0d06caSMauro Carvalho Chehab } 7370c0d06caSMauro Carvalho Chehab 738699b9a86SLaurent Pinchart /* 739699b9a86SLaurent Pinchart * Make sure that the default frame interval stays between 7400c0d06caSMauro Carvalho Chehab * the boundaries. 7410c0d06caSMauro Carvalho Chehab */ 7420c0d06caSMauro Carvalho Chehab n -= frame->bFrameIntervalType ? 1 : 2; 7430c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 7440c0d06caSMauro Carvalho Chehab min(frame->dwFrameInterval[n], 7450c0d06caSMauro Carvalho Chehab max(frame->dwFrameInterval[0], 7460c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval)); 7470c0d06caSMauro Carvalho Chehab 7480c0d06caSMauro Carvalho Chehab if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) { 7490c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = 1; 7500c0d06caSMauro Carvalho Chehab frame->dwFrameInterval[0] = 7510c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval; 7520c0d06caSMauro Carvalho Chehab } 7530c0d06caSMauro Carvalho Chehab 7549e56380aSJoe Perches uvc_dbg(dev, DESCR, "- %ux%u (%u.%u fps)\n", 7550c0d06caSMauro Carvalho Chehab frame->wWidth, frame->wHeight, 7560c0d06caSMauro Carvalho Chehab 10000000 / frame->dwDefaultFrameInterval, 7570c0d06caSMauro Carvalho Chehab (100000000 / frame->dwDefaultFrameInterval) % 10); 7580c0d06caSMauro Carvalho Chehab 7590c0d06caSMauro Carvalho Chehab format->nframes++; 7600c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 7610c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 7620c0d06caSMauro Carvalho Chehab } 7630c0d06caSMauro Carvalho Chehab 7640c0d06caSMauro Carvalho Chehab if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 7650c0d06caSMauro Carvalho Chehab buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { 7660c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 7670c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 7680c0d06caSMauro Carvalho Chehab } 7690c0d06caSMauro Carvalho Chehab 7700c0d06caSMauro Carvalho Chehab if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 7710c0d06caSMauro Carvalho Chehab buffer[2] == UVC_VS_COLORFORMAT) { 7720c0d06caSMauro Carvalho Chehab if (buflen < 6) { 7739e56380aSJoe Perches uvc_dbg(dev, DESCR, 774ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d COLORFORMAT error\n", 7750c0d06caSMauro Carvalho Chehab dev->udev->devnum, 7760c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 7770c0d06caSMauro Carvalho Chehab return -EINVAL; 7780c0d06caSMauro Carvalho Chehab } 7790c0d06caSMauro Carvalho Chehab 7800c0d06caSMauro Carvalho Chehab format->colorspace = uvc_colorspace(buffer[3]); 781ec2c23f6SAdam Goode format->xfer_func = uvc_xfer_func(buffer[4]); 782ec2c23f6SAdam Goode format->ycbcr_enc = uvc_ycbcr_enc(buffer[5]); 7830c0d06caSMauro Carvalho Chehab 7840c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 7850c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 786e82822faSRicardo Ribalda } else { 787e82822faSRicardo Ribalda format->colorspace = V4L2_COLORSPACE_SRGB; 7880c0d06caSMauro Carvalho Chehab } 7890c0d06caSMauro Carvalho Chehab 7900c0d06caSMauro Carvalho Chehab return buffer - start; 7910c0d06caSMauro Carvalho Chehab } 7920c0d06caSMauro Carvalho Chehab 7930c0d06caSMauro Carvalho Chehab static int uvc_parse_streaming(struct uvc_device *dev, 7940c0d06caSMauro Carvalho Chehab struct usb_interface *intf) 7950c0d06caSMauro Carvalho Chehab { 7960c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming = NULL; 7970c0d06caSMauro Carvalho Chehab struct uvc_format *format; 7980c0d06caSMauro Carvalho Chehab struct uvc_frame *frame; 7990c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = &intf->altsetting[0]; 8000c0d06caSMauro Carvalho Chehab unsigned char *_buffer, *buffer = alts->extra; 8010c0d06caSMauro Carvalho Chehab int _buflen, buflen = alts->extralen; 8020c0d06caSMauro Carvalho Chehab unsigned int nformats = 0, nframes = 0, nintervals = 0; 8030c0d06caSMauro Carvalho Chehab unsigned int size, i, n, p; 8042c6b222cSLaurent Pinchart u32 *interval; 8052c6b222cSLaurent Pinchart u16 psize; 8060c0d06caSMauro Carvalho Chehab int ret = -EINVAL; 8070c0d06caSMauro Carvalho Chehab 8080c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass 8090c0d06caSMauro Carvalho Chehab != UVC_SC_VIDEOSTREAMING) { 8109e56380aSJoe Perches uvc_dbg(dev, DESCR, 811ed4c5fa4SRicardo Ribalda "device %d interface %d isn't a video streaming interface\n", 812ed4c5fa4SRicardo Ribalda dev->udev->devnum, 8130c0d06caSMauro Carvalho Chehab intf->altsetting[0].desc.bInterfaceNumber); 8140c0d06caSMauro Carvalho Chehab return -EINVAL; 8150c0d06caSMauro Carvalho Chehab } 8160c0d06caSMauro Carvalho Chehab 8170c0d06caSMauro Carvalho Chehab if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) { 8189e56380aSJoe Perches uvc_dbg(dev, DESCR, 819ed4c5fa4SRicardo Ribalda "device %d interface %d is already claimed\n", 820ed4c5fa4SRicardo Ribalda dev->udev->devnum, 8210c0d06caSMauro Carvalho Chehab intf->altsetting[0].desc.bInterfaceNumber); 8220c0d06caSMauro Carvalho Chehab return -EINVAL; 8230c0d06caSMauro Carvalho Chehab } 8240c0d06caSMauro Carvalho Chehab 825ece41454SKieran Bingham streaming = uvc_stream_new(dev, intf); 8260c0d06caSMauro Carvalho Chehab if (streaming == NULL) { 8270c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, intf); 828ece41454SKieran Bingham return -ENOMEM; 8290c0d06caSMauro Carvalho Chehab } 8300c0d06caSMauro Carvalho Chehab 831699b9a86SLaurent Pinchart /* 832699b9a86SLaurent Pinchart * The Pico iMage webcam has its class-specific interface descriptors 8330c0d06caSMauro Carvalho Chehab * after the endpoint descriptors. 8340c0d06caSMauro Carvalho Chehab */ 8350c0d06caSMauro Carvalho Chehab if (buflen == 0) { 8360c0d06caSMauro Carvalho Chehab for (i = 0; i < alts->desc.bNumEndpoints; ++i) { 8370c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep = &alts->endpoint[i]; 8380c0d06caSMauro Carvalho Chehab 8390c0d06caSMauro Carvalho Chehab if (ep->extralen == 0) 8400c0d06caSMauro Carvalho Chehab continue; 8410c0d06caSMauro Carvalho Chehab 8420c0d06caSMauro Carvalho Chehab if (ep->extralen > 2 && 8430c0d06caSMauro Carvalho Chehab ep->extra[1] == USB_DT_CS_INTERFACE) { 8449e56380aSJoe Perches uvc_dbg(dev, DESCR, 8459e56380aSJoe Perches "trying extra data from endpoint %u\n", 846ed4c5fa4SRicardo Ribalda i); 8470c0d06caSMauro Carvalho Chehab buffer = alts->endpoint[i].extra; 8480c0d06caSMauro Carvalho Chehab buflen = alts->endpoint[i].extralen; 8490c0d06caSMauro Carvalho Chehab break; 8500c0d06caSMauro Carvalho Chehab } 8510c0d06caSMauro Carvalho Chehab } 8520c0d06caSMauro Carvalho Chehab } 8530c0d06caSMauro Carvalho Chehab 8540c0d06caSMauro Carvalho Chehab /* Skip the standard interface descriptors. */ 8550c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) { 8560c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 8570c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 8580c0d06caSMauro Carvalho Chehab } 8590c0d06caSMauro Carvalho Chehab 8600c0d06caSMauro Carvalho Chehab if (buflen <= 2) { 8619e56380aSJoe Perches uvc_dbg(dev, DESCR, 8629e56380aSJoe Perches "no class-specific streaming interface descriptors found\n"); 8630c0d06caSMauro Carvalho Chehab goto error; 8640c0d06caSMauro Carvalho Chehab } 8650c0d06caSMauro Carvalho Chehab 8660c0d06caSMauro Carvalho Chehab /* Parse the header descriptor. */ 8670c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 8680c0d06caSMauro Carvalho Chehab case UVC_VS_OUTPUT_HEADER: 8690c0d06caSMauro Carvalho Chehab streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 8700c0d06caSMauro Carvalho Chehab size = 9; 8710c0d06caSMauro Carvalho Chehab break; 8720c0d06caSMauro Carvalho Chehab 8730c0d06caSMauro Carvalho Chehab case UVC_VS_INPUT_HEADER: 8740c0d06caSMauro Carvalho Chehab streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 8750c0d06caSMauro Carvalho Chehab size = 13; 8760c0d06caSMauro Carvalho Chehab break; 8770c0d06caSMauro Carvalho Chehab 8780c0d06caSMauro Carvalho Chehab default: 8799e56380aSJoe Perches uvc_dbg(dev, DESCR, 8809e56380aSJoe Perches "device %d videostreaming interface %d HEADER descriptor not found\n", 881ed4c5fa4SRicardo Ribalda dev->udev->devnum, alts->desc.bInterfaceNumber); 8820c0d06caSMauro Carvalho Chehab goto error; 8830c0d06caSMauro Carvalho Chehab } 8840c0d06caSMauro Carvalho Chehab 8850c0d06caSMauro Carvalho Chehab p = buflen >= 4 ? buffer[3] : 0; 8860c0d06caSMauro Carvalho Chehab n = buflen >= size ? buffer[size-1] : 0; 8870c0d06caSMauro Carvalho Chehab 8880c0d06caSMauro Carvalho Chehab if (buflen < size + p*n) { 8899e56380aSJoe Perches uvc_dbg(dev, DESCR, 8909e56380aSJoe Perches "device %d videostreaming interface %d HEADER descriptor is invalid\n", 8910c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber); 8920c0d06caSMauro Carvalho Chehab goto error; 8930c0d06caSMauro Carvalho Chehab } 8940c0d06caSMauro Carvalho Chehab 8950c0d06caSMauro Carvalho Chehab streaming->header.bNumFormats = p; 8960c0d06caSMauro Carvalho Chehab streaming->header.bEndpointAddress = buffer[6]; 8970c0d06caSMauro Carvalho Chehab if (buffer[2] == UVC_VS_INPUT_HEADER) { 8980c0d06caSMauro Carvalho Chehab streaming->header.bmInfo = buffer[7]; 8990c0d06caSMauro Carvalho Chehab streaming->header.bTerminalLink = buffer[8]; 9000c0d06caSMauro Carvalho Chehab streaming->header.bStillCaptureMethod = buffer[9]; 9010c0d06caSMauro Carvalho Chehab streaming->header.bTriggerSupport = buffer[10]; 9020c0d06caSMauro Carvalho Chehab streaming->header.bTriggerUsage = buffer[11]; 9030c0d06caSMauro Carvalho Chehab } else { 9040c0d06caSMauro Carvalho Chehab streaming->header.bTerminalLink = buffer[7]; 9050c0d06caSMauro Carvalho Chehab } 9060c0d06caSMauro Carvalho Chehab streaming->header.bControlSize = n; 9070c0d06caSMauro Carvalho Chehab 9080c0d06caSMauro Carvalho Chehab streaming->header.bmaControls = kmemdup(&buffer[size], p * n, 9090c0d06caSMauro Carvalho Chehab GFP_KERNEL); 9100c0d06caSMauro Carvalho Chehab if (streaming->header.bmaControls == NULL) { 9110c0d06caSMauro Carvalho Chehab ret = -ENOMEM; 9120c0d06caSMauro Carvalho Chehab goto error; 9130c0d06caSMauro Carvalho Chehab } 9140c0d06caSMauro Carvalho Chehab 9150c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 9160c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 9170c0d06caSMauro Carvalho Chehab 9180c0d06caSMauro Carvalho Chehab _buffer = buffer; 9190c0d06caSMauro Carvalho Chehab _buflen = buflen; 9200c0d06caSMauro Carvalho Chehab 9210c0d06caSMauro Carvalho Chehab /* Count the format and frame descriptors. */ 9220c0d06caSMauro Carvalho Chehab while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) { 9230c0d06caSMauro Carvalho Chehab switch (_buffer[2]) { 9240c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 9250c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 9260c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 9270c0d06caSMauro Carvalho Chehab nformats++; 9280c0d06caSMauro Carvalho Chehab break; 9290c0d06caSMauro Carvalho Chehab 9300c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 931699b9a86SLaurent Pinchart /* 932699b9a86SLaurent Pinchart * DV format has no frame descriptor. We will create a 9330c0d06caSMauro Carvalho Chehab * dummy frame descriptor with a dummy frame interval. 9340c0d06caSMauro Carvalho Chehab */ 9350c0d06caSMauro Carvalho Chehab nformats++; 9360c0d06caSMauro Carvalho Chehab nframes++; 9370c0d06caSMauro Carvalho Chehab nintervals++; 9380c0d06caSMauro Carvalho Chehab break; 9390c0d06caSMauro Carvalho Chehab 9400c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MPEG2TS: 9410c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_STREAM_BASED: 9429e56380aSJoe Perches uvc_dbg(dev, DESCR, 9439e56380aSJoe Perches "device %d videostreaming interface %d FORMAT %u is not supported\n", 9440c0d06caSMauro Carvalho Chehab dev->udev->devnum, 9450c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, _buffer[2]); 9460c0d06caSMauro Carvalho Chehab break; 9470c0d06caSMauro Carvalho Chehab 9480c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_UNCOMPRESSED: 9490c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_MJPEG: 9500c0d06caSMauro Carvalho Chehab nframes++; 9510c0d06caSMauro Carvalho Chehab if (_buflen > 25) 9520c0d06caSMauro Carvalho Chehab nintervals += _buffer[25] ? _buffer[25] : 3; 9530c0d06caSMauro Carvalho Chehab break; 9540c0d06caSMauro Carvalho Chehab 9550c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_FRAME_BASED: 9560c0d06caSMauro Carvalho Chehab nframes++; 9570c0d06caSMauro Carvalho Chehab if (_buflen > 21) 9580c0d06caSMauro Carvalho Chehab nintervals += _buffer[21] ? _buffer[21] : 3; 9590c0d06caSMauro Carvalho Chehab break; 9600c0d06caSMauro Carvalho Chehab } 9610c0d06caSMauro Carvalho Chehab 9620c0d06caSMauro Carvalho Chehab _buflen -= _buffer[0]; 9630c0d06caSMauro Carvalho Chehab _buffer += _buffer[0]; 9640c0d06caSMauro Carvalho Chehab } 9650c0d06caSMauro Carvalho Chehab 9660c0d06caSMauro Carvalho Chehab if (nformats == 0) { 9679e56380aSJoe Perches uvc_dbg(dev, DESCR, 9689e56380aSJoe Perches "device %d videostreaming interface %d has no supported formats defined\n", 9690c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber); 9700c0d06caSMauro Carvalho Chehab goto error; 9710c0d06caSMauro Carvalho Chehab } 9720c0d06caSMauro Carvalho Chehab 973f14d4988SLaurent Pinchart size = nformats * sizeof(*format) + nframes * sizeof(*frame) 974f14d4988SLaurent Pinchart + nintervals * sizeof(*interval); 9750c0d06caSMauro Carvalho Chehab format = kzalloc(size, GFP_KERNEL); 9760c0d06caSMauro Carvalho Chehab if (format == NULL) { 9770c0d06caSMauro Carvalho Chehab ret = -ENOMEM; 9780c0d06caSMauro Carvalho Chehab goto error; 9790c0d06caSMauro Carvalho Chehab } 9800c0d06caSMauro Carvalho Chehab 9810c0d06caSMauro Carvalho Chehab frame = (struct uvc_frame *)&format[nformats]; 9822c6b222cSLaurent Pinchart interval = (u32 *)&frame[nframes]; 9830c0d06caSMauro Carvalho Chehab 9840c0d06caSMauro Carvalho Chehab streaming->format = format; 9850c0d06caSMauro Carvalho Chehab streaming->nformats = nformats; 9860c0d06caSMauro Carvalho Chehab 9870c0d06caSMauro Carvalho Chehab /* Parse the format descriptors. */ 9880c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) { 9890c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 9900c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 9910c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 9920c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 9930c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 9940c0d06caSMauro Carvalho Chehab format->frame = frame; 9950c0d06caSMauro Carvalho Chehab ret = uvc_parse_format(dev, streaming, format, 9960c0d06caSMauro Carvalho Chehab &interval, buffer, buflen); 9970c0d06caSMauro Carvalho Chehab if (ret < 0) 9980c0d06caSMauro Carvalho Chehab goto error; 9990c0d06caSMauro Carvalho Chehab 10000c0d06caSMauro Carvalho Chehab frame += format->nframes; 10010c0d06caSMauro Carvalho Chehab format++; 10020c0d06caSMauro Carvalho Chehab 10030c0d06caSMauro Carvalho Chehab buflen -= ret; 10040c0d06caSMauro Carvalho Chehab buffer += ret; 10050c0d06caSMauro Carvalho Chehab continue; 10060c0d06caSMauro Carvalho Chehab 10070c0d06caSMauro Carvalho Chehab default: 10080c0d06caSMauro Carvalho Chehab break; 10090c0d06caSMauro Carvalho Chehab } 10100c0d06caSMauro Carvalho Chehab 10110c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 10120c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 10130c0d06caSMauro Carvalho Chehab } 10140c0d06caSMauro Carvalho Chehab 10150c0d06caSMauro Carvalho Chehab if (buflen) 10169e56380aSJoe Perches uvc_dbg(dev, DESCR, 10179e56380aSJoe Perches "device %d videostreaming interface %d has %u bytes of trailing descriptor garbage\n", 10189e56380aSJoe Perches dev->udev->devnum, alts->desc.bInterfaceNumber, buflen); 10190c0d06caSMauro Carvalho Chehab 10200c0d06caSMauro Carvalho Chehab /* Parse the alternate settings to find the maximum bandwidth. */ 10210c0d06caSMauro Carvalho Chehab for (i = 0; i < intf->num_altsetting; ++i) { 10220c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep; 10230c0d06caSMauro Carvalho Chehab alts = &intf->altsetting[i]; 10240c0d06caSMauro Carvalho Chehab ep = uvc_find_endpoint(alts, 10250c0d06caSMauro Carvalho Chehab streaming->header.bEndpointAddress); 10260c0d06caSMauro Carvalho Chehab if (ep == NULL) 10270c0d06caSMauro Carvalho Chehab continue; 10285b9c75c7SRicardo Ribalda psize = uvc_endpoint_max_bpi(dev->udev, ep); 10290c0d06caSMauro Carvalho Chehab if (psize > streaming->maxpsize) 10300c0d06caSMauro Carvalho Chehab streaming->maxpsize = psize; 10310c0d06caSMauro Carvalho Chehab } 10320c0d06caSMauro Carvalho Chehab 10330c0d06caSMauro Carvalho Chehab list_add_tail(&streaming->list, &dev->streams); 10340c0d06caSMauro Carvalho Chehab return 0; 10350c0d06caSMauro Carvalho Chehab 10360c0d06caSMauro Carvalho Chehab error: 10370c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, intf); 1038ece41454SKieran Bingham uvc_stream_delete(streaming); 10390c0d06caSMauro Carvalho Chehab return ret; 10400c0d06caSMauro Carvalho Chehab } 10410c0d06caSMauro Carvalho Chehab 1042351509c6SRicardo Ribalda static const u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA; 10432886477fSRicardo Ribalda static const u8 uvc_gpio_guid[16] = UVC_GUID_EXT_GPIO_CONTROLLER; 1044351509c6SRicardo Ribalda static const u8 uvc_media_transport_input_guid[16] = 1045351509c6SRicardo Ribalda UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT; 1046351509c6SRicardo Ribalda static const u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING; 1047351509c6SRicardo Ribalda 1048cae79e50SRicardo Ribalda static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id, 10490c0d06caSMauro Carvalho Chehab unsigned int num_pads, unsigned int extra_size) 10500c0d06caSMauro Carvalho Chehab { 10510c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 10520c0d06caSMauro Carvalho Chehab unsigned int num_inputs; 10530c0d06caSMauro Carvalho Chehab unsigned int size; 10540c0d06caSMauro Carvalho Chehab unsigned int i; 10550c0d06caSMauro Carvalho Chehab 105689dd34caSNadav Amit extra_size = roundup(extra_size, sizeof(*entity->pads)); 10577532dad6SRicardo Ribalda if (num_pads) 10587532dad6SRicardo Ribalda num_inputs = type & UVC_TERM_OUTPUT ? num_pads : num_pads - 1; 10597532dad6SRicardo Ribalda else 10607532dad6SRicardo Ribalda num_inputs = 0; 10610c0d06caSMauro Carvalho Chehab size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads 10620c0d06caSMauro Carvalho Chehab + num_inputs; 10630c0d06caSMauro Carvalho Chehab entity = kzalloc(size, GFP_KERNEL); 10640c0d06caSMauro Carvalho Chehab if (entity == NULL) 10650c0d06caSMauro Carvalho Chehab return NULL; 10660c0d06caSMauro Carvalho Chehab 10670c0d06caSMauro Carvalho Chehab entity->id = id; 10680c0d06caSMauro Carvalho Chehab entity->type = type; 10690c0d06caSMauro Carvalho Chehab 1070351509c6SRicardo Ribalda /* 1071351509c6SRicardo Ribalda * Set the GUID for standard entity types. For extension units, the GUID 1072351509c6SRicardo Ribalda * is initialized by the caller. 1073351509c6SRicardo Ribalda */ 1074351509c6SRicardo Ribalda switch (type) { 10752886477fSRicardo Ribalda case UVC_EXT_GPIO_UNIT: 10762886477fSRicardo Ribalda memcpy(entity->guid, uvc_gpio_guid, 16); 10772886477fSRicardo Ribalda break; 1078351509c6SRicardo Ribalda case UVC_ITT_CAMERA: 1079351509c6SRicardo Ribalda memcpy(entity->guid, uvc_camera_guid, 16); 1080351509c6SRicardo Ribalda break; 1081351509c6SRicardo Ribalda case UVC_ITT_MEDIA_TRANSPORT_INPUT: 1082351509c6SRicardo Ribalda memcpy(entity->guid, uvc_media_transport_input_guid, 16); 1083351509c6SRicardo Ribalda break; 1084351509c6SRicardo Ribalda case UVC_VC_PROCESSING_UNIT: 1085351509c6SRicardo Ribalda memcpy(entity->guid, uvc_processing_guid, 16); 1086351509c6SRicardo Ribalda break; 1087351509c6SRicardo Ribalda } 1088351509c6SRicardo Ribalda 10890c0d06caSMauro Carvalho Chehab entity->num_links = 0; 10900c0d06caSMauro Carvalho Chehab entity->num_pads = num_pads; 10910c0d06caSMauro Carvalho Chehab entity->pads = ((void *)(entity + 1)) + extra_size; 10920c0d06caSMauro Carvalho Chehab 10930c0d06caSMauro Carvalho Chehab for (i = 0; i < num_inputs; ++i) 10940c0d06caSMauro Carvalho Chehab entity->pads[i].flags = MEDIA_PAD_FL_SINK; 10957532dad6SRicardo Ribalda if (!UVC_ENTITY_IS_OTERM(entity) && num_pads) 10960c0d06caSMauro Carvalho Chehab entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE; 10970c0d06caSMauro Carvalho Chehab 10980c0d06caSMauro Carvalho Chehab entity->bNrInPins = num_inputs; 10992c6b222cSLaurent Pinchart entity->baSourceID = (u8 *)(&entity->pads[num_pads]); 11000c0d06caSMauro Carvalho Chehab 11010c0d06caSMauro Carvalho Chehab return entity; 11020c0d06caSMauro Carvalho Chehab } 11030c0d06caSMauro Carvalho Chehab 11040c0d06caSMauro Carvalho Chehab /* Parse vendor-specific extensions. */ 11050c0d06caSMauro Carvalho Chehab static int uvc_parse_vendor_control(struct uvc_device *dev, 11060c0d06caSMauro Carvalho Chehab const unsigned char *buffer, int buflen) 11070c0d06caSMauro Carvalho Chehab { 11080c0d06caSMauro Carvalho Chehab struct usb_device *udev = dev->udev; 11090c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 11100c0d06caSMauro Carvalho Chehab struct uvc_entity *unit; 11110c0d06caSMauro Carvalho Chehab unsigned int n, p; 11120c0d06caSMauro Carvalho Chehab int handled = 0; 11130c0d06caSMauro Carvalho Chehab 11140c0d06caSMauro Carvalho Chehab switch (le16_to_cpu(dev->udev->descriptor.idVendor)) { 11150c0d06caSMauro Carvalho Chehab case 0x046d: /* Logitech */ 11160c0d06caSMauro Carvalho Chehab if (buffer[1] != 0x41 || buffer[2] != 0x01) 11170c0d06caSMauro Carvalho Chehab break; 11180c0d06caSMauro Carvalho Chehab 1119699b9a86SLaurent Pinchart /* 1120699b9a86SLaurent Pinchart * Logitech implements several vendor specific functions 11210c0d06caSMauro Carvalho Chehab * through vendor specific extension units (LXU). 11220c0d06caSMauro Carvalho Chehab * 11230c0d06caSMauro Carvalho Chehab * The LXU descriptors are similar to XU descriptors 11240c0d06caSMauro Carvalho Chehab * (see "USB Device Video Class for Video Devices", section 11250c0d06caSMauro Carvalho Chehab * 3.7.2.6 "Extension Unit Descriptor") with the following 11260c0d06caSMauro Carvalho Chehab * differences: 11270c0d06caSMauro Carvalho Chehab * 11280c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 11290c0d06caSMauro Carvalho Chehab * 0 bLength 1 Number 11300c0d06caSMauro Carvalho Chehab * Size of this descriptor, in bytes: 24+p+n*2 11310c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 11320c0d06caSMauro Carvalho Chehab * 23+p+n bmControlsType N Bitmap 11330c0d06caSMauro Carvalho Chehab * Individual bits in the set are defined: 11340c0d06caSMauro Carvalho Chehab * 0: Absolute 11350c0d06caSMauro Carvalho Chehab * 1: Relative 11360c0d06caSMauro Carvalho Chehab * 11370c0d06caSMauro Carvalho Chehab * This bitset is mapped exactly the same as bmControls. 11380c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 11390c0d06caSMauro Carvalho Chehab * 23+p+n*2 bReserved 1 Boolean 11400c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 11410c0d06caSMauro Carvalho Chehab * 24+p+n*2 iExtension 1 Index 11420c0d06caSMauro Carvalho Chehab * Index of a string descriptor that describes this 11430c0d06caSMauro Carvalho Chehab * extension unit. 11440c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 11450c0d06caSMauro Carvalho Chehab */ 11460c0d06caSMauro Carvalho Chehab p = buflen >= 22 ? buffer[21] : 0; 11470c0d06caSMauro Carvalho Chehab n = buflen >= 25 + p ? buffer[22+p] : 0; 11480c0d06caSMauro Carvalho Chehab 11490c0d06caSMauro Carvalho Chehab if (buflen < 25 + p + 2*n) { 11509e56380aSJoe Perches uvc_dbg(dev, DESCR, 1151ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d EXTENSION_UNIT error\n", 11520c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 11530c0d06caSMauro Carvalho Chehab break; 11540c0d06caSMauro Carvalho Chehab } 11550c0d06caSMauro Carvalho Chehab 11560c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3], 11570c0d06caSMauro Carvalho Chehab p + 1, 2*n); 11580c0d06caSMauro Carvalho Chehab if (unit == NULL) 11590c0d06caSMauro Carvalho Chehab return -ENOMEM; 11600c0d06caSMauro Carvalho Chehab 1161351509c6SRicardo Ribalda memcpy(unit->guid, &buffer[4], 16); 11620c0d06caSMauro Carvalho Chehab unit->extension.bNumControls = buffer[20]; 11630c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[22], p); 11640c0d06caSMauro Carvalho Chehab unit->extension.bControlSize = buffer[22+p]; 11652c6b222cSLaurent Pinchart unit->extension.bmControls = (u8 *)unit + sizeof(*unit); 11662c6b222cSLaurent Pinchart unit->extension.bmControlsType = (u8 *)unit + sizeof(*unit) 11670c0d06caSMauro Carvalho Chehab + n; 11680c0d06caSMauro Carvalho Chehab memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); 11690c0d06caSMauro Carvalho Chehab 11700c0d06caSMauro Carvalho Chehab if (buffer[24+p+2*n] != 0) 11710c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[24+p+2*n], unit->name, 1172f14d4988SLaurent Pinchart sizeof(unit->name)); 11730c0d06caSMauro Carvalho Chehab else 11740c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Extension %u", buffer[3]); 11750c0d06caSMauro Carvalho Chehab 11760c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 11770c0d06caSMauro Carvalho Chehab handled = 1; 11780c0d06caSMauro Carvalho Chehab break; 11790c0d06caSMauro Carvalho Chehab } 11800c0d06caSMauro Carvalho Chehab 11810c0d06caSMauro Carvalho Chehab return handled; 11820c0d06caSMauro Carvalho Chehab } 11830c0d06caSMauro Carvalho Chehab 11840c0d06caSMauro Carvalho Chehab static int uvc_parse_standard_control(struct uvc_device *dev, 11850c0d06caSMauro Carvalho Chehab const unsigned char *buffer, int buflen) 11860c0d06caSMauro Carvalho Chehab { 11870c0d06caSMauro Carvalho Chehab struct usb_device *udev = dev->udev; 11880c0d06caSMauro Carvalho Chehab struct uvc_entity *unit, *term; 11890c0d06caSMauro Carvalho Chehab struct usb_interface *intf; 11900c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 11910c0d06caSMauro Carvalho Chehab unsigned int i, n, p, len; 11922c6b222cSLaurent Pinchart u16 type; 11930c0d06caSMauro Carvalho Chehab 11940c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 11950c0d06caSMauro Carvalho Chehab case UVC_VC_HEADER: 11960c0d06caSMauro Carvalho Chehab n = buflen >= 12 ? buffer[11] : 0; 11970c0d06caSMauro Carvalho Chehab 1198daf41ac2SOliver Neukum if (buflen < 12 + n) { 11999e56380aSJoe Perches uvc_dbg(dev, DESCR, 1200ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d HEADER error\n", 1201ed4c5fa4SRicardo Ribalda udev->devnum, alts->desc.bInterfaceNumber); 12020c0d06caSMauro Carvalho Chehab return -EINVAL; 12030c0d06caSMauro Carvalho Chehab } 12040c0d06caSMauro Carvalho Chehab 12050c0d06caSMauro Carvalho Chehab dev->uvc_version = get_unaligned_le16(&buffer[3]); 12060c0d06caSMauro Carvalho Chehab dev->clock_frequency = get_unaligned_le32(&buffer[7]); 12070c0d06caSMauro Carvalho Chehab 12080c0d06caSMauro Carvalho Chehab /* Parse all USB Video Streaming interfaces. */ 12090c0d06caSMauro Carvalho Chehab for (i = 0; i < n; ++i) { 12100c0d06caSMauro Carvalho Chehab intf = usb_ifnum_to_if(udev, buffer[12+i]); 12110c0d06caSMauro Carvalho Chehab if (intf == NULL) { 12129e56380aSJoe Perches uvc_dbg(dev, DESCR, 1213ed4c5fa4SRicardo Ribalda "device %d interface %d doesn't exists\n", 12140c0d06caSMauro Carvalho Chehab udev->devnum, i); 12150c0d06caSMauro Carvalho Chehab continue; 12160c0d06caSMauro Carvalho Chehab } 12170c0d06caSMauro Carvalho Chehab 12180c0d06caSMauro Carvalho Chehab uvc_parse_streaming(dev, intf); 12190c0d06caSMauro Carvalho Chehab } 12200c0d06caSMauro Carvalho Chehab break; 12210c0d06caSMauro Carvalho Chehab 12220c0d06caSMauro Carvalho Chehab case UVC_VC_INPUT_TERMINAL: 12230c0d06caSMauro Carvalho Chehab if (buflen < 8) { 12249e56380aSJoe Perches uvc_dbg(dev, DESCR, 1225ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d INPUT_TERMINAL error\n", 12260c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 12270c0d06caSMauro Carvalho Chehab return -EINVAL; 12280c0d06caSMauro Carvalho Chehab } 12290c0d06caSMauro Carvalho Chehab 123047bb1179SAlistair Strachan /* 123147bb1179SAlistair Strachan * Reject invalid terminal types that would cause issues: 123247bb1179SAlistair Strachan * 123347bb1179SAlistair Strachan * - The high byte must be non-zero, otherwise it would be 123447bb1179SAlistair Strachan * confused with a unit. 123547bb1179SAlistair Strachan * 123647bb1179SAlistair Strachan * - Bit 15 must be 0, as we use it internally as a terminal 123747bb1179SAlistair Strachan * direction flag. 123847bb1179SAlistair Strachan * 123947bb1179SAlistair Strachan * Other unknown types are accepted. 12400c0d06caSMauro Carvalho Chehab */ 12410c0d06caSMauro Carvalho Chehab type = get_unaligned_le16(&buffer[4]); 124247bb1179SAlistair Strachan if ((type & 0x7f00) == 0 || (type & 0x8000) != 0) { 12439e56380aSJoe Perches uvc_dbg(dev, DESCR, 1244ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d INPUT_TERMINAL %d has invalid type 0x%04x, skipping\n", 1245ed4c5fa4SRicardo Ribalda udev->devnum, alts->desc.bInterfaceNumber, 12460c0d06caSMauro Carvalho Chehab buffer[3], type); 12470c0d06caSMauro Carvalho Chehab return 0; 12480c0d06caSMauro Carvalho Chehab } 12490c0d06caSMauro Carvalho Chehab 12500c0d06caSMauro Carvalho Chehab n = 0; 12510c0d06caSMauro Carvalho Chehab p = 0; 12520c0d06caSMauro Carvalho Chehab len = 8; 12530c0d06caSMauro Carvalho Chehab 12540c0d06caSMauro Carvalho Chehab if (type == UVC_ITT_CAMERA) { 12550c0d06caSMauro Carvalho Chehab n = buflen >= 15 ? buffer[14] : 0; 12560c0d06caSMauro Carvalho Chehab len = 15; 12570c0d06caSMauro Carvalho Chehab 12580c0d06caSMauro Carvalho Chehab } else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) { 12590c0d06caSMauro Carvalho Chehab n = buflen >= 9 ? buffer[8] : 0; 12600c0d06caSMauro Carvalho Chehab p = buflen >= 10 + n ? buffer[9+n] : 0; 12610c0d06caSMauro Carvalho Chehab len = 10; 12620c0d06caSMauro Carvalho Chehab } 12630c0d06caSMauro Carvalho Chehab 12640c0d06caSMauro Carvalho Chehab if (buflen < len + n + p) { 12659e56380aSJoe Perches uvc_dbg(dev, DESCR, 1266ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d INPUT_TERMINAL error\n", 12670c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 12680c0d06caSMauro Carvalho Chehab return -EINVAL; 12690c0d06caSMauro Carvalho Chehab } 12700c0d06caSMauro Carvalho Chehab 12710c0d06caSMauro Carvalho Chehab term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3], 12720c0d06caSMauro Carvalho Chehab 1, n + p); 12730c0d06caSMauro Carvalho Chehab if (term == NULL) 12740c0d06caSMauro Carvalho Chehab return -ENOMEM; 12750c0d06caSMauro Carvalho Chehab 12760c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) { 12770c0d06caSMauro Carvalho Chehab term->camera.bControlSize = n; 1278f14d4988SLaurent Pinchart term->camera.bmControls = (u8 *)term + sizeof(*term); 12790c0d06caSMauro Carvalho Chehab term->camera.wObjectiveFocalLengthMin = 12800c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[8]); 12810c0d06caSMauro Carvalho Chehab term->camera.wObjectiveFocalLengthMax = 12820c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[10]); 12830c0d06caSMauro Carvalho Chehab term->camera.wOcularFocalLength = 12840c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[12]); 12850c0d06caSMauro Carvalho Chehab memcpy(term->camera.bmControls, &buffer[15], n); 12860c0d06caSMauro Carvalho Chehab } else if (UVC_ENTITY_TYPE(term) == 12870c0d06caSMauro Carvalho Chehab UVC_ITT_MEDIA_TRANSPORT_INPUT) { 12880c0d06caSMauro Carvalho Chehab term->media.bControlSize = n; 1289f14d4988SLaurent Pinchart term->media.bmControls = (u8 *)term + sizeof(*term); 12900c0d06caSMauro Carvalho Chehab term->media.bTransportModeSize = p; 12912c6b222cSLaurent Pinchart term->media.bmTransportModes = (u8 *)term 1292f14d4988SLaurent Pinchart + sizeof(*term) + n; 12930c0d06caSMauro Carvalho Chehab memcpy(term->media.bmControls, &buffer[9], n); 12940c0d06caSMauro Carvalho Chehab memcpy(term->media.bmTransportModes, &buffer[10+n], p); 12950c0d06caSMauro Carvalho Chehab } 12960c0d06caSMauro Carvalho Chehab 12970c0d06caSMauro Carvalho Chehab if (buffer[7] != 0) 12980c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[7], term->name, 1299f14d4988SLaurent Pinchart sizeof(term->name)); 13000c0d06caSMauro Carvalho Chehab else if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) 13010c0d06caSMauro Carvalho Chehab sprintf(term->name, "Camera %u", buffer[3]); 13020c0d06caSMauro Carvalho Chehab else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT) 13030c0d06caSMauro Carvalho Chehab sprintf(term->name, "Media %u", buffer[3]); 13040c0d06caSMauro Carvalho Chehab else 13050c0d06caSMauro Carvalho Chehab sprintf(term->name, "Input %u", buffer[3]); 13060c0d06caSMauro Carvalho Chehab 13070c0d06caSMauro Carvalho Chehab list_add_tail(&term->list, &dev->entities); 13080c0d06caSMauro Carvalho Chehab break; 13090c0d06caSMauro Carvalho Chehab 13100c0d06caSMauro Carvalho Chehab case UVC_VC_OUTPUT_TERMINAL: 13110c0d06caSMauro Carvalho Chehab if (buflen < 9) { 13129e56380aSJoe Perches uvc_dbg(dev, DESCR, 1313ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d OUTPUT_TERMINAL error\n", 13140c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 13150c0d06caSMauro Carvalho Chehab return -EINVAL; 13160c0d06caSMauro Carvalho Chehab } 13170c0d06caSMauro Carvalho Chehab 1318699b9a86SLaurent Pinchart /* 1319699b9a86SLaurent Pinchart * Make sure the terminal type MSB is not null, otherwise it 13200c0d06caSMauro Carvalho Chehab * could be confused with a unit. 13210c0d06caSMauro Carvalho Chehab */ 13220c0d06caSMauro Carvalho Chehab type = get_unaligned_le16(&buffer[4]); 13230c0d06caSMauro Carvalho Chehab if ((type & 0xff00) == 0) { 13249e56380aSJoe Perches uvc_dbg(dev, DESCR, 1325ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d OUTPUT_TERMINAL %d has invalid type 0x%04x, skipping\n", 1326ed4c5fa4SRicardo Ribalda udev->devnum, alts->desc.bInterfaceNumber, 1327ed4c5fa4SRicardo Ribalda buffer[3], type); 13280c0d06caSMauro Carvalho Chehab return 0; 13290c0d06caSMauro Carvalho Chehab } 13300c0d06caSMauro Carvalho Chehab 13310c0d06caSMauro Carvalho Chehab term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3], 13320c0d06caSMauro Carvalho Chehab 1, 0); 13330c0d06caSMauro Carvalho Chehab if (term == NULL) 13340c0d06caSMauro Carvalho Chehab return -ENOMEM; 13350c0d06caSMauro Carvalho Chehab 13360c0d06caSMauro Carvalho Chehab memcpy(term->baSourceID, &buffer[7], 1); 13370c0d06caSMauro Carvalho Chehab 13380c0d06caSMauro Carvalho Chehab if (buffer[8] != 0) 13390c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[8], term->name, 1340f14d4988SLaurent Pinchart sizeof(term->name)); 13410c0d06caSMauro Carvalho Chehab else 13420c0d06caSMauro Carvalho Chehab sprintf(term->name, "Output %u", buffer[3]); 13430c0d06caSMauro Carvalho Chehab 13440c0d06caSMauro Carvalho Chehab list_add_tail(&term->list, &dev->entities); 13450c0d06caSMauro Carvalho Chehab break; 13460c0d06caSMauro Carvalho Chehab 13470c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 13480c0d06caSMauro Carvalho Chehab p = buflen >= 5 ? buffer[4] : 0; 13490c0d06caSMauro Carvalho Chehab 13500c0d06caSMauro Carvalho Chehab if (buflen < 5 || buflen < 6 + p) { 13519e56380aSJoe Perches uvc_dbg(dev, DESCR, 1352ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d SELECTOR_UNIT error\n", 13530c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 13540c0d06caSMauro Carvalho Chehab return -EINVAL; 13550c0d06caSMauro Carvalho Chehab } 13560c0d06caSMauro Carvalho Chehab 13570c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0); 13580c0d06caSMauro Carvalho Chehab if (unit == NULL) 13590c0d06caSMauro Carvalho Chehab return -ENOMEM; 13600c0d06caSMauro Carvalho Chehab 13610c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[5], p); 13620c0d06caSMauro Carvalho Chehab 13630c0d06caSMauro Carvalho Chehab if (buffer[5+p] != 0) 13640c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[5+p], unit->name, 1365f14d4988SLaurent Pinchart sizeof(unit->name)); 13660c0d06caSMauro Carvalho Chehab else 13670c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Selector %u", buffer[3]); 13680c0d06caSMauro Carvalho Chehab 13690c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 13700c0d06caSMauro Carvalho Chehab break; 13710c0d06caSMauro Carvalho Chehab 13720c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 13730c0d06caSMauro Carvalho Chehab n = buflen >= 8 ? buffer[7] : 0; 13740c0d06caSMauro Carvalho Chehab p = dev->uvc_version >= 0x0110 ? 10 : 9; 13750c0d06caSMauro Carvalho Chehab 13760c0d06caSMauro Carvalho Chehab if (buflen < p + n) { 13779e56380aSJoe Perches uvc_dbg(dev, DESCR, 1378ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d PROCESSING_UNIT error\n", 13790c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 13800c0d06caSMauro Carvalho Chehab return -EINVAL; 13810c0d06caSMauro Carvalho Chehab } 13820c0d06caSMauro Carvalho Chehab 13830c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n); 13840c0d06caSMauro Carvalho Chehab if (unit == NULL) 13850c0d06caSMauro Carvalho Chehab return -ENOMEM; 13860c0d06caSMauro Carvalho Chehab 13870c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[4], 1); 13880c0d06caSMauro Carvalho Chehab unit->processing.wMaxMultiplier = 13890c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[5]); 13900c0d06caSMauro Carvalho Chehab unit->processing.bControlSize = buffer[7]; 1391f14d4988SLaurent Pinchart unit->processing.bmControls = (u8 *)unit + sizeof(*unit); 13920c0d06caSMauro Carvalho Chehab memcpy(unit->processing.bmControls, &buffer[8], n); 13930c0d06caSMauro Carvalho Chehab if (dev->uvc_version >= 0x0110) 13940c0d06caSMauro Carvalho Chehab unit->processing.bmVideoStandards = buffer[9+n]; 13950c0d06caSMauro Carvalho Chehab 13960c0d06caSMauro Carvalho Chehab if (buffer[8+n] != 0) 13970c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[8+n], unit->name, 1398f14d4988SLaurent Pinchart sizeof(unit->name)); 13990c0d06caSMauro Carvalho Chehab else 14000c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Processing %u", buffer[3]); 14010c0d06caSMauro Carvalho Chehab 14020c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 14030c0d06caSMauro Carvalho Chehab break; 14040c0d06caSMauro Carvalho Chehab 14050c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 14060c0d06caSMauro Carvalho Chehab p = buflen >= 22 ? buffer[21] : 0; 14070c0d06caSMauro Carvalho Chehab n = buflen >= 24 + p ? buffer[22+p] : 0; 14080c0d06caSMauro Carvalho Chehab 14090c0d06caSMauro Carvalho Chehab if (buflen < 24 + p + n) { 14109e56380aSJoe Perches uvc_dbg(dev, DESCR, 1411ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d EXTENSION_UNIT error\n", 14120c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 14130c0d06caSMauro Carvalho Chehab return -EINVAL; 14140c0d06caSMauro Carvalho Chehab } 14150c0d06caSMauro Carvalho Chehab 14160c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n); 14170c0d06caSMauro Carvalho Chehab if (unit == NULL) 14180c0d06caSMauro Carvalho Chehab return -ENOMEM; 14190c0d06caSMauro Carvalho Chehab 1420351509c6SRicardo Ribalda memcpy(unit->guid, &buffer[4], 16); 14210c0d06caSMauro Carvalho Chehab unit->extension.bNumControls = buffer[20]; 14220c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[22], p); 14230c0d06caSMauro Carvalho Chehab unit->extension.bControlSize = buffer[22+p]; 1424f14d4988SLaurent Pinchart unit->extension.bmControls = (u8 *)unit + sizeof(*unit); 14250c0d06caSMauro Carvalho Chehab memcpy(unit->extension.bmControls, &buffer[23+p], n); 14260c0d06caSMauro Carvalho Chehab 14270c0d06caSMauro Carvalho Chehab if (buffer[23+p+n] != 0) 14280c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[23+p+n], unit->name, 1429f14d4988SLaurent Pinchart sizeof(unit->name)); 14300c0d06caSMauro Carvalho Chehab else 14310c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Extension %u", buffer[3]); 14320c0d06caSMauro Carvalho Chehab 14330c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 14340c0d06caSMauro Carvalho Chehab break; 14350c0d06caSMauro Carvalho Chehab 14360c0d06caSMauro Carvalho Chehab default: 14379e56380aSJoe Perches uvc_dbg(dev, DESCR, 1438ed4c5fa4SRicardo Ribalda "Found an unknown CS_INTERFACE descriptor (%u)\n", 1439ed4c5fa4SRicardo Ribalda buffer[2]); 14400c0d06caSMauro Carvalho Chehab break; 14410c0d06caSMauro Carvalho Chehab } 14420c0d06caSMauro Carvalho Chehab 14430c0d06caSMauro Carvalho Chehab return 0; 14440c0d06caSMauro Carvalho Chehab } 14450c0d06caSMauro Carvalho Chehab 14460c0d06caSMauro Carvalho Chehab static int uvc_parse_control(struct uvc_device *dev) 14470c0d06caSMauro Carvalho Chehab { 14480c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 14490c0d06caSMauro Carvalho Chehab unsigned char *buffer = alts->extra; 14500c0d06caSMauro Carvalho Chehab int buflen = alts->extralen; 14510c0d06caSMauro Carvalho Chehab int ret; 14520c0d06caSMauro Carvalho Chehab 1453699b9a86SLaurent Pinchart /* 1454699b9a86SLaurent Pinchart * Parse the default alternate setting only, as the UVC specification 14550c0d06caSMauro Carvalho Chehab * defines a single alternate setting, the default alternate setting 14560c0d06caSMauro Carvalho Chehab * zero. 14570c0d06caSMauro Carvalho Chehab */ 14580c0d06caSMauro Carvalho Chehab 14590c0d06caSMauro Carvalho Chehab while (buflen > 2) { 14600c0d06caSMauro Carvalho Chehab if (uvc_parse_vendor_control(dev, buffer, buflen) || 14610c0d06caSMauro Carvalho Chehab buffer[1] != USB_DT_CS_INTERFACE) 14620c0d06caSMauro Carvalho Chehab goto next_descriptor; 14630c0d06caSMauro Carvalho Chehab 14640c0d06caSMauro Carvalho Chehab if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0) 14650c0d06caSMauro Carvalho Chehab return ret; 14660c0d06caSMauro Carvalho Chehab 14670c0d06caSMauro Carvalho Chehab next_descriptor: 14680c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 14690c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 14700c0d06caSMauro Carvalho Chehab } 14710c0d06caSMauro Carvalho Chehab 1472699b9a86SLaurent Pinchart /* 1473699b9a86SLaurent Pinchart * Check if the optional status endpoint is present. Built-in iSight 14740c0d06caSMauro Carvalho Chehab * webcams have an interrupt endpoint but spit proprietary data that 14750c0d06caSMauro Carvalho Chehab * don't conform to the UVC status endpoint messages. Don't try to 14760c0d06caSMauro Carvalho Chehab * handle the interrupt endpoint for those cameras. 14770c0d06caSMauro Carvalho Chehab */ 14780c0d06caSMauro Carvalho Chehab if (alts->desc.bNumEndpoints == 1 && 14790c0d06caSMauro Carvalho Chehab !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) { 14800c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep = &alts->endpoint[0]; 14810c0d06caSMauro Carvalho Chehab struct usb_endpoint_descriptor *desc = &ep->desc; 14820c0d06caSMauro Carvalho Chehab 14830c0d06caSMauro Carvalho Chehab if (usb_endpoint_is_int_in(desc) && 14840c0d06caSMauro Carvalho Chehab le16_to_cpu(desc->wMaxPacketSize) >= 8 && 14850c0d06caSMauro Carvalho Chehab desc->bInterval != 0) { 14869e56380aSJoe Perches uvc_dbg(dev, DESCR, 14879e56380aSJoe Perches "Found a Status endpoint (addr %02x)\n", 1488ed4c5fa4SRicardo Ribalda desc->bEndpointAddress); 14890c0d06caSMauro Carvalho Chehab dev->int_ep = ep; 14900c0d06caSMauro Carvalho Chehab } 14910c0d06caSMauro Carvalho Chehab } 14920c0d06caSMauro Carvalho Chehab 14930c0d06caSMauro Carvalho Chehab return 0; 14940c0d06caSMauro Carvalho Chehab } 14950c0d06caSMauro Carvalho Chehab 14962886477fSRicardo Ribalda /* ----------------------------------------------------------------------------- 14972886477fSRicardo Ribalda * Privacy GPIO 14982886477fSRicardo Ribalda */ 14992886477fSRicardo Ribalda 15002886477fSRicardo Ribalda static void uvc_gpio_event(struct uvc_device *dev) 15012886477fSRicardo Ribalda { 15022886477fSRicardo Ribalda struct uvc_entity *unit = dev->gpio_unit; 15032886477fSRicardo Ribalda struct uvc_video_chain *chain; 15042886477fSRicardo Ribalda u8 new_val; 15052886477fSRicardo Ribalda 15062886477fSRicardo Ribalda if (!unit) 15072886477fSRicardo Ribalda return; 15082886477fSRicardo Ribalda 15092886477fSRicardo Ribalda new_val = gpiod_get_value_cansleep(unit->gpio.gpio_privacy); 15102886477fSRicardo Ribalda 15112886477fSRicardo Ribalda /* GPIO entities are always on the first chain. */ 15122886477fSRicardo Ribalda chain = list_first_entry(&dev->chains, struct uvc_video_chain, list); 15132886477fSRicardo Ribalda uvc_ctrl_status_event(chain, unit->controls, &new_val); 15142886477fSRicardo Ribalda } 15152886477fSRicardo Ribalda 15162886477fSRicardo Ribalda static int uvc_gpio_get_cur(struct uvc_device *dev, struct uvc_entity *entity, 15172886477fSRicardo Ribalda u8 cs, void *data, u16 size) 15182886477fSRicardo Ribalda { 15192886477fSRicardo Ribalda if (cs != UVC_CT_PRIVACY_CONTROL || size < 1) 15202886477fSRicardo Ribalda return -EINVAL; 15212886477fSRicardo Ribalda 15222886477fSRicardo Ribalda *(u8 *)data = gpiod_get_value_cansleep(entity->gpio.gpio_privacy); 15232886477fSRicardo Ribalda 15242886477fSRicardo Ribalda return 0; 15252886477fSRicardo Ribalda } 15262886477fSRicardo Ribalda 15272886477fSRicardo Ribalda static int uvc_gpio_get_info(struct uvc_device *dev, struct uvc_entity *entity, 15282886477fSRicardo Ribalda u8 cs, u8 *caps) 15292886477fSRicardo Ribalda { 15302886477fSRicardo Ribalda if (cs != UVC_CT_PRIVACY_CONTROL) 15312886477fSRicardo Ribalda return -EINVAL; 15322886477fSRicardo Ribalda 15332886477fSRicardo Ribalda *caps = UVC_CONTROL_CAP_GET | UVC_CONTROL_CAP_AUTOUPDATE; 15342886477fSRicardo Ribalda return 0; 15352886477fSRicardo Ribalda } 15362886477fSRicardo Ribalda 15372886477fSRicardo Ribalda static irqreturn_t uvc_gpio_irq(int irq, void *data) 15382886477fSRicardo Ribalda { 15392886477fSRicardo Ribalda struct uvc_device *dev = data; 15402886477fSRicardo Ribalda 15412886477fSRicardo Ribalda uvc_gpio_event(dev); 15422886477fSRicardo Ribalda return IRQ_HANDLED; 15432886477fSRicardo Ribalda } 15442886477fSRicardo Ribalda 15452886477fSRicardo Ribalda static int uvc_gpio_parse(struct uvc_device *dev) 15462886477fSRicardo Ribalda { 15472886477fSRicardo Ribalda struct uvc_entity *unit; 15482886477fSRicardo Ribalda struct gpio_desc *gpio_privacy; 15492886477fSRicardo Ribalda int irq; 15502886477fSRicardo Ribalda 15512886477fSRicardo Ribalda gpio_privacy = devm_gpiod_get_optional(&dev->udev->dev, "privacy", 15522886477fSRicardo Ribalda GPIOD_IN); 15532886477fSRicardo Ribalda if (IS_ERR_OR_NULL(gpio_privacy)) 15542886477fSRicardo Ribalda return PTR_ERR_OR_ZERO(gpio_privacy); 15552886477fSRicardo Ribalda 15562886477fSRicardo Ribalda irq = gpiod_to_irq(gpio_privacy); 15572886477fSRicardo Ribalda if (irq < 0) { 15582886477fSRicardo Ribalda if (irq != EPROBE_DEFER) 15592886477fSRicardo Ribalda dev_err(&dev->udev->dev, 15602886477fSRicardo Ribalda "No IRQ for privacy GPIO (%d)\n", irq); 15612886477fSRicardo Ribalda return irq; 15622886477fSRicardo Ribalda } 15632886477fSRicardo Ribalda 1564f0f07845SJosé Expósito unit = uvc_alloc_entity(UVC_EXT_GPIO_UNIT, UVC_EXT_GPIO_UNIT_ID, 0, 1); 1565f0f07845SJosé Expósito if (!unit) 1566f0f07845SJosé Expósito return -ENOMEM; 1567f0f07845SJosé Expósito 15682886477fSRicardo Ribalda unit->gpio.gpio_privacy = gpio_privacy; 15692886477fSRicardo Ribalda unit->gpio.irq = irq; 15702886477fSRicardo Ribalda unit->gpio.bControlSize = 1; 15712886477fSRicardo Ribalda unit->gpio.bmControls = (u8 *)unit + sizeof(*unit); 15722886477fSRicardo Ribalda unit->gpio.bmControls[0] = 1; 15732886477fSRicardo Ribalda unit->get_cur = uvc_gpio_get_cur; 15742886477fSRicardo Ribalda unit->get_info = uvc_gpio_get_info; 1575063b811fSHans Verkuil strscpy(unit->name, "GPIO", sizeof(unit->name)); 15762886477fSRicardo Ribalda 15772886477fSRicardo Ribalda list_add_tail(&unit->list, &dev->entities); 15782886477fSRicardo Ribalda 15792886477fSRicardo Ribalda dev->gpio_unit = unit; 15802886477fSRicardo Ribalda 15812886477fSRicardo Ribalda return 0; 15822886477fSRicardo Ribalda } 15832886477fSRicardo Ribalda 15842886477fSRicardo Ribalda static int uvc_gpio_init_irq(struct uvc_device *dev) 15852886477fSRicardo Ribalda { 15862886477fSRicardo Ribalda struct uvc_entity *unit = dev->gpio_unit; 15872886477fSRicardo Ribalda 15882886477fSRicardo Ribalda if (!unit || unit->gpio.irq < 0) 15892886477fSRicardo Ribalda return 0; 15902886477fSRicardo Ribalda 15912886477fSRicardo Ribalda return devm_request_threaded_irq(&dev->udev->dev, unit->gpio.irq, NULL, 15922886477fSRicardo Ribalda uvc_gpio_irq, 15932886477fSRicardo Ribalda IRQF_ONESHOT | IRQF_TRIGGER_FALLING | 15942886477fSRicardo Ribalda IRQF_TRIGGER_RISING, 15952886477fSRicardo Ribalda "uvc_privacy_gpio", dev); 15962886477fSRicardo Ribalda } 15972886477fSRicardo Ribalda 15980c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 15990c0d06caSMauro Carvalho Chehab * UVC device scan 16000c0d06caSMauro Carvalho Chehab */ 16010c0d06caSMauro Carvalho Chehab 16020c0d06caSMauro Carvalho Chehab /* 16030c0d06caSMauro Carvalho Chehab * Scan the UVC descriptors to locate a chain starting at an Output Terminal 16040c0d06caSMauro Carvalho Chehab * and containing the following units: 16050c0d06caSMauro Carvalho Chehab * 16060c0d06caSMauro Carvalho Chehab * - one or more Output Terminals (USB Streaming or Display) 16070c0d06caSMauro Carvalho Chehab * - zero or one Processing Unit 16080c0d06caSMauro Carvalho Chehab * - zero, one or more single-input Selector Units 16090c0d06caSMauro Carvalho Chehab * - zero or one multiple-input Selector Units, provided all inputs are 16100c0d06caSMauro Carvalho Chehab * connected to input terminals 16110c0d06caSMauro Carvalho Chehab * - zero, one or mode single-input Extension Units 16120c0d06caSMauro Carvalho Chehab * - one or more Input Terminals (Camera, External or USB Streaming) 16130c0d06caSMauro Carvalho Chehab * 16140c0d06caSMauro Carvalho Chehab * The terminal and units must match on of the following structures: 16150c0d06caSMauro Carvalho Chehab * 16160c0d06caSMauro Carvalho Chehab * ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0) 16170c0d06caSMauro Carvalho Chehab * ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ... 16180c0d06caSMauro Carvalho Chehab * ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n) 16190c0d06caSMauro Carvalho Chehab * 16200c0d06caSMauro Carvalho Chehab * +---------+ +---------+ -> OTT_*(0) 16210c0d06caSMauro Carvalho Chehab * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ... 16220c0d06caSMauro Carvalho Chehab * +---------+ +---------+ -> OTT_*(n) 16230c0d06caSMauro Carvalho Chehab * 16240c0d06caSMauro Carvalho Chehab * The Processing Unit and Extension Units can be in any order. Additional 16250c0d06caSMauro Carvalho Chehab * Extension Units connected to the main chain as single-unit branches are 16260c0d06caSMauro Carvalho Chehab * also supported. Single-input Selector Units are ignored. 16270c0d06caSMauro Carvalho Chehab */ 16280c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_entity(struct uvc_video_chain *chain, 16290c0d06caSMauro Carvalho Chehab struct uvc_entity *entity) 16300c0d06caSMauro Carvalho Chehab { 16310c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(entity)) { 16320c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 16339e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- XU %d", entity->id); 16340c0d06caSMauro Carvalho Chehab 16350c0d06caSMauro Carvalho Chehab if (entity->bNrInPins != 1) { 16369e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 16379e56380aSJoe Perches "Extension unit %d has more than 1 input pin\n", 1638ed4c5fa4SRicardo Ribalda entity->id); 16390c0d06caSMauro Carvalho Chehab return -1; 16400c0d06caSMauro Carvalho Chehab } 16410c0d06caSMauro Carvalho Chehab 16420c0d06caSMauro Carvalho Chehab break; 16430c0d06caSMauro Carvalho Chehab 16440c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 16459e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- PU %d", entity->id); 16460c0d06caSMauro Carvalho Chehab 16470c0d06caSMauro Carvalho Chehab if (chain->processing != NULL) { 16489e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 16499e56380aSJoe Perches "Found multiple Processing Units in chain\n"); 16500c0d06caSMauro Carvalho Chehab return -1; 16510c0d06caSMauro Carvalho Chehab } 16520c0d06caSMauro Carvalho Chehab 16530c0d06caSMauro Carvalho Chehab chain->processing = entity; 16540c0d06caSMauro Carvalho Chehab break; 16550c0d06caSMauro Carvalho Chehab 16560c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 16579e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- SU %d", entity->id); 16580c0d06caSMauro Carvalho Chehab 16590c0d06caSMauro Carvalho Chehab /* Single-input selector units are ignored. */ 16600c0d06caSMauro Carvalho Chehab if (entity->bNrInPins == 1) 16610c0d06caSMauro Carvalho Chehab break; 16620c0d06caSMauro Carvalho Chehab 16630c0d06caSMauro Carvalho Chehab if (chain->selector != NULL) { 16649e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 16659e56380aSJoe Perches "Found multiple Selector Units in chain\n"); 16660c0d06caSMauro Carvalho Chehab return -1; 16670c0d06caSMauro Carvalho Chehab } 16680c0d06caSMauro Carvalho Chehab 16690c0d06caSMauro Carvalho Chehab chain->selector = entity; 16700c0d06caSMauro Carvalho Chehab break; 16710c0d06caSMauro Carvalho Chehab 16720c0d06caSMauro Carvalho Chehab case UVC_ITT_VENDOR_SPECIFIC: 16730c0d06caSMauro Carvalho Chehab case UVC_ITT_CAMERA: 16740c0d06caSMauro Carvalho Chehab case UVC_ITT_MEDIA_TRANSPORT_INPUT: 16759e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- IT %d\n", entity->id); 16760c0d06caSMauro Carvalho Chehab 16770c0d06caSMauro Carvalho Chehab break; 16780c0d06caSMauro Carvalho Chehab 16790c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 16800c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 16810c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 16829e56380aSJoe Perches uvc_dbg_cont(PROBE, " OT %d", entity->id); 16830c0d06caSMauro Carvalho Chehab 16840c0d06caSMauro Carvalho Chehab break; 16850c0d06caSMauro Carvalho Chehab 16860c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 168759e92bf6SRicardo Ribalda if (UVC_ENTITY_IS_ITERM(entity)) 16889e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- IT %d\n", entity->id); 168959e92bf6SRicardo Ribalda else 16909e56380aSJoe Perches uvc_dbg_cont(PROBE, " OT %d", entity->id); 16910c0d06caSMauro Carvalho Chehab 16920c0d06caSMauro Carvalho Chehab break; 16930c0d06caSMauro Carvalho Chehab 16940c0d06caSMauro Carvalho Chehab default: 16959e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 16969e56380aSJoe Perches "Unsupported entity type 0x%04x found in chain\n", 1697ed4c5fa4SRicardo Ribalda UVC_ENTITY_TYPE(entity)); 16980c0d06caSMauro Carvalho Chehab return -1; 16990c0d06caSMauro Carvalho Chehab } 17000c0d06caSMauro Carvalho Chehab 17010c0d06caSMauro Carvalho Chehab list_add_tail(&entity->chain, &chain->entities); 17020c0d06caSMauro Carvalho Chehab return 0; 17030c0d06caSMauro Carvalho Chehab } 17040c0d06caSMauro Carvalho Chehab 17050c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_forward(struct uvc_video_chain *chain, 17060c0d06caSMauro Carvalho Chehab struct uvc_entity *entity, struct uvc_entity *prev) 17070c0d06caSMauro Carvalho Chehab { 17080c0d06caSMauro Carvalho Chehab struct uvc_entity *forward; 17090c0d06caSMauro Carvalho Chehab int found; 17100c0d06caSMauro Carvalho Chehab 17110c0d06caSMauro Carvalho Chehab /* Forward scan */ 17120c0d06caSMauro Carvalho Chehab forward = NULL; 17130c0d06caSMauro Carvalho Chehab found = 0; 17140c0d06caSMauro Carvalho Chehab 17150c0d06caSMauro Carvalho Chehab while (1) { 17160c0d06caSMauro Carvalho Chehab forward = uvc_entity_by_reference(chain->dev, entity->id, 17170c0d06caSMauro Carvalho Chehab forward); 17180c0d06caSMauro Carvalho Chehab if (forward == NULL) 17190c0d06caSMauro Carvalho Chehab break; 17200c0d06caSMauro Carvalho Chehab if (forward == prev) 17210c0d06caSMauro Carvalho Chehab continue; 172268035c80SWill Deacon if (forward->chain.next || forward->chain.prev) { 17239e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 17249e56380aSJoe Perches "Found reference to entity %d already in chain\n", 1725ed4c5fa4SRicardo Ribalda forward->id); 172668035c80SWill Deacon return -EINVAL; 172768035c80SWill Deacon } 17280c0d06caSMauro Carvalho Chehab 17290c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(forward)) { 17300c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 17310c0d06caSMauro Carvalho Chehab if (forward->bNrInPins != 1) { 17329e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 17339e56380aSJoe Perches "Extension unit %d has more than 1 input pin\n", 173432934486SLaurent Pinchart forward->id); 17350c0d06caSMauro Carvalho Chehab return -EINVAL; 17360c0d06caSMauro Carvalho Chehab } 17370c0d06caSMauro Carvalho Chehab 17384ca052b4SLaurent Pinchart /* 17394ca052b4SLaurent Pinchart * Some devices reference an output terminal as the 17404ca052b4SLaurent Pinchart * source of extension units. This is incorrect, as 17414ca052b4SLaurent Pinchart * output terminals only have an input pin, and thus 17424ca052b4SLaurent Pinchart * can't be connected to any entity in the forward 17434ca052b4SLaurent Pinchart * direction. The resulting topology would cause issues 17444ca052b4SLaurent Pinchart * when registering the media controller graph. To 17454ca052b4SLaurent Pinchart * avoid this problem, connect the extension unit to 17464ca052b4SLaurent Pinchart * the source of the output terminal instead. 17474ca052b4SLaurent Pinchart */ 17484ca052b4SLaurent Pinchart if (UVC_ENTITY_IS_OTERM(entity)) { 17494ca052b4SLaurent Pinchart struct uvc_entity *source; 17504ca052b4SLaurent Pinchart 17514ca052b4SLaurent Pinchart source = uvc_entity_by_id(chain->dev, 17524ca052b4SLaurent Pinchart entity->baSourceID[0]); 17534ca052b4SLaurent Pinchart if (!source) { 17544ca052b4SLaurent Pinchart uvc_dbg(chain->dev, DESCR, 17554ca052b4SLaurent Pinchart "Can't connect extension unit %u in chain\n", 17564ca052b4SLaurent Pinchart forward->id); 17574ca052b4SLaurent Pinchart break; 17584ca052b4SLaurent Pinchart } 17594ca052b4SLaurent Pinchart 17604ca052b4SLaurent Pinchart forward->baSourceID[0] = source->id; 17614ca052b4SLaurent Pinchart } 17624ca052b4SLaurent Pinchart 17630c0d06caSMauro Carvalho Chehab list_add_tail(&forward->chain, &chain->entities); 17640c0d06caSMauro Carvalho Chehab if (!found) 17659e56380aSJoe Perches uvc_dbg_cont(PROBE, " (->"); 17660c0d06caSMauro Carvalho Chehab 17679e56380aSJoe Perches uvc_dbg_cont(PROBE, " XU %d", forward->id); 17680c0d06caSMauro Carvalho Chehab found = 1; 17690c0d06caSMauro Carvalho Chehab break; 17700c0d06caSMauro Carvalho Chehab 17710c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 17720c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 17730c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 17740c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 17750c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_IS_ITERM(forward)) { 17769e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 17779e56380aSJoe Perches "Unsupported input terminal %u\n", 1778ed4c5fa4SRicardo Ribalda forward->id); 17790c0d06caSMauro Carvalho Chehab return -EINVAL; 17800c0d06caSMauro Carvalho Chehab } 17810c0d06caSMauro Carvalho Chehab 17824ca052b4SLaurent Pinchart if (UVC_ENTITY_IS_OTERM(entity)) { 17834ca052b4SLaurent Pinchart uvc_dbg(chain->dev, DESCR, 17844ca052b4SLaurent Pinchart "Unsupported connection between output terminals %u and %u\n", 17854ca052b4SLaurent Pinchart entity->id, forward->id); 17864ca052b4SLaurent Pinchart break; 17874ca052b4SLaurent Pinchart } 17884ca052b4SLaurent Pinchart 17890c0d06caSMauro Carvalho Chehab list_add_tail(&forward->chain, &chain->entities); 17900c0d06caSMauro Carvalho Chehab if (!found) 17919e56380aSJoe Perches uvc_dbg_cont(PROBE, " (->"); 17920c0d06caSMauro Carvalho Chehab 17939e56380aSJoe Perches uvc_dbg_cont(PROBE, " OT %d", forward->id); 17940c0d06caSMauro Carvalho Chehab found = 1; 17950c0d06caSMauro Carvalho Chehab break; 17960c0d06caSMauro Carvalho Chehab } 17970c0d06caSMauro Carvalho Chehab } 17980c0d06caSMauro Carvalho Chehab if (found) 17999e56380aSJoe Perches uvc_dbg_cont(PROBE, ")"); 18000c0d06caSMauro Carvalho Chehab 18010c0d06caSMauro Carvalho Chehab return 0; 18020c0d06caSMauro Carvalho Chehab } 18030c0d06caSMauro Carvalho Chehab 18040c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_backward(struct uvc_video_chain *chain, 18050c0d06caSMauro Carvalho Chehab struct uvc_entity **_entity) 18060c0d06caSMauro Carvalho Chehab { 18070c0d06caSMauro Carvalho Chehab struct uvc_entity *entity = *_entity; 18080c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 18090c0d06caSMauro Carvalho Chehab int id = -EINVAL, i; 18100c0d06caSMauro Carvalho Chehab 18110c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(entity)) { 18120c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 18130c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 18140c0d06caSMauro Carvalho Chehab id = entity->baSourceID[0]; 18150c0d06caSMauro Carvalho Chehab break; 18160c0d06caSMauro Carvalho Chehab 18170c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 18180c0d06caSMauro Carvalho Chehab /* Single-input selector units are ignored. */ 18190c0d06caSMauro Carvalho Chehab if (entity->bNrInPins == 1) { 18200c0d06caSMauro Carvalho Chehab id = entity->baSourceID[0]; 18210c0d06caSMauro Carvalho Chehab break; 18220c0d06caSMauro Carvalho Chehab } 18230c0d06caSMauro Carvalho Chehab 18249e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- IT"); 18250c0d06caSMauro Carvalho Chehab 18260c0d06caSMauro Carvalho Chehab chain->selector = entity; 18270c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->bNrInPins; ++i) { 18280c0d06caSMauro Carvalho Chehab id = entity->baSourceID[i]; 18290c0d06caSMauro Carvalho Chehab term = uvc_entity_by_id(chain->dev, id); 18300c0d06caSMauro Carvalho Chehab if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { 18319e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 1832ed4c5fa4SRicardo Ribalda "Selector unit %d input %d isn't connected to an input terminal\n", 1833ed4c5fa4SRicardo Ribalda entity->id, i); 18340c0d06caSMauro Carvalho Chehab return -1; 18350c0d06caSMauro Carvalho Chehab } 18360c0d06caSMauro Carvalho Chehab 183768035c80SWill Deacon if (term->chain.next || term->chain.prev) { 18389e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 18399e56380aSJoe Perches "Found reference to entity %d already in chain\n", 184068035c80SWill Deacon term->id); 184168035c80SWill Deacon return -EINVAL; 184268035c80SWill Deacon } 184368035c80SWill Deacon 18449e56380aSJoe Perches uvc_dbg_cont(PROBE, " %d", term->id); 18450c0d06caSMauro Carvalho Chehab 18460c0d06caSMauro Carvalho Chehab list_add_tail(&term->chain, &chain->entities); 18470c0d06caSMauro Carvalho Chehab uvc_scan_chain_forward(chain, term, entity); 18480c0d06caSMauro Carvalho Chehab } 18490c0d06caSMauro Carvalho Chehab 18509e56380aSJoe Perches uvc_dbg_cont(PROBE, "\n"); 18510c0d06caSMauro Carvalho Chehab 18520c0d06caSMauro Carvalho Chehab id = 0; 18530c0d06caSMauro Carvalho Chehab break; 18540c0d06caSMauro Carvalho Chehab 18550c0d06caSMauro Carvalho Chehab case UVC_ITT_VENDOR_SPECIFIC: 18560c0d06caSMauro Carvalho Chehab case UVC_ITT_CAMERA: 18570c0d06caSMauro Carvalho Chehab case UVC_ITT_MEDIA_TRANSPORT_INPUT: 18580c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 18590c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 18600c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 18610c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 18620c0d06caSMauro Carvalho Chehab id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0; 18630c0d06caSMauro Carvalho Chehab break; 18640c0d06caSMauro Carvalho Chehab } 18650c0d06caSMauro Carvalho Chehab 18660c0d06caSMauro Carvalho Chehab if (id <= 0) { 18670c0d06caSMauro Carvalho Chehab *_entity = NULL; 18680c0d06caSMauro Carvalho Chehab return id; 18690c0d06caSMauro Carvalho Chehab } 18700c0d06caSMauro Carvalho Chehab 18710c0d06caSMauro Carvalho Chehab entity = uvc_entity_by_id(chain->dev, id); 18720c0d06caSMauro Carvalho Chehab if (entity == NULL) { 18739e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 18749e56380aSJoe Perches "Found reference to unknown entity %d\n", id); 18750c0d06caSMauro Carvalho Chehab return -EINVAL; 18760c0d06caSMauro Carvalho Chehab } 18770c0d06caSMauro Carvalho Chehab 18780c0d06caSMauro Carvalho Chehab *_entity = entity; 18790c0d06caSMauro Carvalho Chehab return 0; 18800c0d06caSMauro Carvalho Chehab } 18810c0d06caSMauro Carvalho Chehab 18820c0d06caSMauro Carvalho Chehab static int uvc_scan_chain(struct uvc_video_chain *chain, 18830c0d06caSMauro Carvalho Chehab struct uvc_entity *term) 18840c0d06caSMauro Carvalho Chehab { 18850c0d06caSMauro Carvalho Chehab struct uvc_entity *entity, *prev; 18860c0d06caSMauro Carvalho Chehab 18879e56380aSJoe Perches uvc_dbg(chain->dev, PROBE, "Scanning UVC chain:"); 18880c0d06caSMauro Carvalho Chehab 18890c0d06caSMauro Carvalho Chehab entity = term; 18900c0d06caSMauro Carvalho Chehab prev = NULL; 18910c0d06caSMauro Carvalho Chehab 18920c0d06caSMauro Carvalho Chehab while (entity != NULL) { 18930c0d06caSMauro Carvalho Chehab /* Entity must not be part of an existing chain */ 18940c0d06caSMauro Carvalho Chehab if (entity->chain.next || entity->chain.prev) { 18959e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 18969e56380aSJoe Perches "Found reference to entity %d already in chain\n", 1897ed4c5fa4SRicardo Ribalda entity->id); 18980c0d06caSMauro Carvalho Chehab return -EINVAL; 18990c0d06caSMauro Carvalho Chehab } 19000c0d06caSMauro Carvalho Chehab 19010c0d06caSMauro Carvalho Chehab /* Process entity */ 19020c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_entity(chain, entity) < 0) 19030c0d06caSMauro Carvalho Chehab return -EINVAL; 19040c0d06caSMauro Carvalho Chehab 19050c0d06caSMauro Carvalho Chehab /* Forward scan */ 19060c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_forward(chain, entity, prev) < 0) 19070c0d06caSMauro Carvalho Chehab return -EINVAL; 19080c0d06caSMauro Carvalho Chehab 19090c0d06caSMauro Carvalho Chehab /* Backward scan */ 19100c0d06caSMauro Carvalho Chehab prev = entity; 19110c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_backward(chain, &entity) < 0) 19120c0d06caSMauro Carvalho Chehab return -EINVAL; 19130c0d06caSMauro Carvalho Chehab } 19140c0d06caSMauro Carvalho Chehab 19150c0d06caSMauro Carvalho Chehab return 0; 19160c0d06caSMauro Carvalho Chehab } 19170c0d06caSMauro Carvalho Chehab 19180c0d06caSMauro Carvalho Chehab static unsigned int uvc_print_terms(struct list_head *terms, u16 dir, 19190c0d06caSMauro Carvalho Chehab char *buffer) 19200c0d06caSMauro Carvalho Chehab { 19210c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 19220c0d06caSMauro Carvalho Chehab unsigned int nterms = 0; 19230c0d06caSMauro Carvalho Chehab char *p = buffer; 19240c0d06caSMauro Carvalho Chehab 19250c0d06caSMauro Carvalho Chehab list_for_each_entry(term, terms, chain) { 19260c0d06caSMauro Carvalho Chehab if (!UVC_ENTITY_IS_TERM(term) || 19270c0d06caSMauro Carvalho Chehab UVC_TERM_DIRECTION(term) != dir) 19280c0d06caSMauro Carvalho Chehab continue; 19290c0d06caSMauro Carvalho Chehab 19300c0d06caSMauro Carvalho Chehab if (nterms) 19310c0d06caSMauro Carvalho Chehab p += sprintf(p, ","); 19320c0d06caSMauro Carvalho Chehab if (++nterms >= 4) { 19330c0d06caSMauro Carvalho Chehab p += sprintf(p, "..."); 19340c0d06caSMauro Carvalho Chehab break; 19350c0d06caSMauro Carvalho Chehab } 19360c0d06caSMauro Carvalho Chehab p += sprintf(p, "%u", term->id); 19370c0d06caSMauro Carvalho Chehab } 19380c0d06caSMauro Carvalho Chehab 19390c0d06caSMauro Carvalho Chehab return p - buffer; 19400c0d06caSMauro Carvalho Chehab } 19410c0d06caSMauro Carvalho Chehab 19420c0d06caSMauro Carvalho Chehab static const char *uvc_print_chain(struct uvc_video_chain *chain) 19430c0d06caSMauro Carvalho Chehab { 19440c0d06caSMauro Carvalho Chehab static char buffer[43]; 19450c0d06caSMauro Carvalho Chehab char *p = buffer; 19460c0d06caSMauro Carvalho Chehab 19470c0d06caSMauro Carvalho Chehab p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p); 19480c0d06caSMauro Carvalho Chehab p += sprintf(p, " -> "); 19490c0d06caSMauro Carvalho Chehab uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p); 19500c0d06caSMauro Carvalho Chehab 19510c0d06caSMauro Carvalho Chehab return buffer; 19520c0d06caSMauro Carvalho Chehab } 19530c0d06caSMauro Carvalho Chehab 1954e950267aSHenrik Ingo static struct uvc_video_chain *uvc_alloc_chain(struct uvc_device *dev) 1955e950267aSHenrik Ingo { 1956e950267aSHenrik Ingo struct uvc_video_chain *chain; 1957e950267aSHenrik Ingo 1958e950267aSHenrik Ingo chain = kzalloc(sizeof(*chain), GFP_KERNEL); 1959e950267aSHenrik Ingo if (chain == NULL) 1960e950267aSHenrik Ingo return NULL; 1961e950267aSHenrik Ingo 1962e950267aSHenrik Ingo INIT_LIST_HEAD(&chain->entities); 1963e950267aSHenrik Ingo mutex_init(&chain->ctrl_mutex); 1964e950267aSHenrik Ingo chain->dev = dev; 1965e950267aSHenrik Ingo v4l2_prio_init(&chain->prio); 1966e950267aSHenrik Ingo 1967e950267aSHenrik Ingo return chain; 1968e950267aSHenrik Ingo } 1969e950267aSHenrik Ingo 1970e950267aSHenrik Ingo /* 1971e950267aSHenrik Ingo * Fallback heuristic for devices that don't connect units and terminals in a 1972e950267aSHenrik Ingo * valid chain. 1973e950267aSHenrik Ingo * 1974e950267aSHenrik Ingo * Some devices have invalid baSourceID references, causing uvc_scan_chain() 1975e950267aSHenrik Ingo * to fail, but if we just take the entities we can find and put them together 1976e950267aSHenrik Ingo * in the most sensible chain we can think of, turns out they do work anyway. 1977e950267aSHenrik Ingo * Note: This heuristic assumes there is a single chain. 1978e950267aSHenrik Ingo * 1979e950267aSHenrik Ingo * At the time of writing, devices known to have such a broken chain are 1980e950267aSHenrik Ingo * - Acer Integrated Camera (5986:055a) 1981e950267aSHenrik Ingo * - Realtek rtl157a7 (0bda:57a7) 1982e950267aSHenrik Ingo */ 1983e950267aSHenrik Ingo static int uvc_scan_fallback(struct uvc_device *dev) 1984e950267aSHenrik Ingo { 1985e950267aSHenrik Ingo struct uvc_video_chain *chain; 1986e950267aSHenrik Ingo struct uvc_entity *iterm = NULL; 1987e950267aSHenrik Ingo struct uvc_entity *oterm = NULL; 1988e950267aSHenrik Ingo struct uvc_entity *entity; 1989e950267aSHenrik Ingo struct uvc_entity *prev; 1990e950267aSHenrik Ingo 1991e950267aSHenrik Ingo /* 1992e950267aSHenrik Ingo * Start by locating the input and output terminals. We only support 1993e950267aSHenrik Ingo * devices with exactly one of each for now. 1994e950267aSHenrik Ingo */ 1995e950267aSHenrik Ingo list_for_each_entry(entity, &dev->entities, list) { 1996e950267aSHenrik Ingo if (UVC_ENTITY_IS_ITERM(entity)) { 1997e950267aSHenrik Ingo if (iterm) 1998e950267aSHenrik Ingo return -EINVAL; 1999e950267aSHenrik Ingo iterm = entity; 2000e950267aSHenrik Ingo } 2001e950267aSHenrik Ingo 2002e950267aSHenrik Ingo if (UVC_ENTITY_IS_OTERM(entity)) { 2003e950267aSHenrik Ingo if (oterm) 2004e950267aSHenrik Ingo return -EINVAL; 2005e950267aSHenrik Ingo oterm = entity; 2006e950267aSHenrik Ingo } 2007e950267aSHenrik Ingo } 2008e950267aSHenrik Ingo 2009e950267aSHenrik Ingo if (iterm == NULL || oterm == NULL) 2010e950267aSHenrik Ingo return -EINVAL; 2011e950267aSHenrik Ingo 2012e950267aSHenrik Ingo /* Allocate the chain and fill it. */ 2013e950267aSHenrik Ingo chain = uvc_alloc_chain(dev); 2014e950267aSHenrik Ingo if (chain == NULL) 2015e950267aSHenrik Ingo return -ENOMEM; 2016e950267aSHenrik Ingo 2017e950267aSHenrik Ingo if (uvc_scan_chain_entity(chain, oterm) < 0) 2018e950267aSHenrik Ingo goto error; 2019e950267aSHenrik Ingo 2020e950267aSHenrik Ingo prev = oterm; 2021e950267aSHenrik Ingo 2022e950267aSHenrik Ingo /* 2023e950267aSHenrik Ingo * Add all Processing and Extension Units with two pads. The order 2024e950267aSHenrik Ingo * doesn't matter much, use reverse list traversal to connect units in 2025e950267aSHenrik Ingo * UVC descriptor order as we build the chain from output to input. This 2026e950267aSHenrik Ingo * leads to units appearing in the order meant by the manufacturer for 2027e950267aSHenrik Ingo * the cameras known to require this heuristic. 2028e950267aSHenrik Ingo */ 2029e950267aSHenrik Ingo list_for_each_entry_reverse(entity, &dev->entities, list) { 2030e950267aSHenrik Ingo if (entity->type != UVC_VC_PROCESSING_UNIT && 2031e950267aSHenrik Ingo entity->type != UVC_VC_EXTENSION_UNIT) 2032e950267aSHenrik Ingo continue; 2033e950267aSHenrik Ingo 2034e950267aSHenrik Ingo if (entity->num_pads != 2) 2035e950267aSHenrik Ingo continue; 2036e950267aSHenrik Ingo 2037e950267aSHenrik Ingo if (uvc_scan_chain_entity(chain, entity) < 0) 2038e950267aSHenrik Ingo goto error; 2039e950267aSHenrik Ingo 2040e950267aSHenrik Ingo prev->baSourceID[0] = entity->id; 2041e950267aSHenrik Ingo prev = entity; 2042e950267aSHenrik Ingo } 2043e950267aSHenrik Ingo 2044e950267aSHenrik Ingo if (uvc_scan_chain_entity(chain, iterm) < 0) 2045e950267aSHenrik Ingo goto error; 2046e950267aSHenrik Ingo 2047e950267aSHenrik Ingo prev->baSourceID[0] = iterm->id; 2048e950267aSHenrik Ingo 2049e950267aSHenrik Ingo list_add_tail(&chain->list, &dev->chains); 2050e950267aSHenrik Ingo 20519e56380aSJoe Perches uvc_dbg(dev, PROBE, "Found a video chain by fallback heuristic (%s)\n", 2052e950267aSHenrik Ingo uvc_print_chain(chain)); 2053e950267aSHenrik Ingo 2054e950267aSHenrik Ingo return 0; 2055e950267aSHenrik Ingo 2056e950267aSHenrik Ingo error: 2057e950267aSHenrik Ingo kfree(chain); 2058e950267aSHenrik Ingo return -EINVAL; 2059e950267aSHenrik Ingo } 2060e950267aSHenrik Ingo 20610c0d06caSMauro Carvalho Chehab /* 20620c0d06caSMauro Carvalho Chehab * Scan the device for video chains and register video devices. 20630c0d06caSMauro Carvalho Chehab * 20640c0d06caSMauro Carvalho Chehab * Chains are scanned starting at their output terminals and walked backwards. 20650c0d06caSMauro Carvalho Chehab */ 20660c0d06caSMauro Carvalho Chehab static int uvc_scan_device(struct uvc_device *dev) 20670c0d06caSMauro Carvalho Chehab { 20680c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 20690c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 20700c0d06caSMauro Carvalho Chehab 20710c0d06caSMauro Carvalho Chehab list_for_each_entry(term, &dev->entities, list) { 20720c0d06caSMauro Carvalho Chehab if (!UVC_ENTITY_IS_OTERM(term)) 20730c0d06caSMauro Carvalho Chehab continue; 20740c0d06caSMauro Carvalho Chehab 2075699b9a86SLaurent Pinchart /* 2076699b9a86SLaurent Pinchart * If the terminal is already included in a chain, skip it. 20770c0d06caSMauro Carvalho Chehab * This can happen for chains that have multiple output 20780c0d06caSMauro Carvalho Chehab * terminals, where all output terminals beside the first one 20790c0d06caSMauro Carvalho Chehab * will be inserted in the chain in forward scans. 20800c0d06caSMauro Carvalho Chehab */ 20810c0d06caSMauro Carvalho Chehab if (term->chain.next || term->chain.prev) 20820c0d06caSMauro Carvalho Chehab continue; 20830c0d06caSMauro Carvalho Chehab 2084e950267aSHenrik Ingo chain = uvc_alloc_chain(dev); 20850c0d06caSMauro Carvalho Chehab if (chain == NULL) 20860c0d06caSMauro Carvalho Chehab return -ENOMEM; 20870c0d06caSMauro Carvalho Chehab 20888be8ec6eSLaurent Pinchart term->flags |= UVC_ENTITY_FLAG_DEFAULT; 20898be8ec6eSLaurent Pinchart 20900c0d06caSMauro Carvalho Chehab if (uvc_scan_chain(chain, term) < 0) { 20910c0d06caSMauro Carvalho Chehab kfree(chain); 20920c0d06caSMauro Carvalho Chehab continue; 20930c0d06caSMauro Carvalho Chehab } 20940c0d06caSMauro Carvalho Chehab 20959e56380aSJoe Perches uvc_dbg(dev, PROBE, "Found a valid video chain (%s)\n", 20960c0d06caSMauro Carvalho Chehab uvc_print_chain(chain)); 20970c0d06caSMauro Carvalho Chehab 20980c0d06caSMauro Carvalho Chehab list_add_tail(&chain->list, &dev->chains); 20990c0d06caSMauro Carvalho Chehab } 21000c0d06caSMauro Carvalho Chehab 2101e950267aSHenrik Ingo if (list_empty(&dev->chains)) 2102e950267aSHenrik Ingo uvc_scan_fallback(dev); 2103e950267aSHenrik Ingo 21040c0d06caSMauro Carvalho Chehab if (list_empty(&dev->chains)) { 210569df0954SRicardo Ribalda dev_info(&dev->udev->dev, "No valid video chain found.\n"); 21060c0d06caSMauro Carvalho Chehab return -1; 21070c0d06caSMauro Carvalho Chehab } 21080c0d06caSMauro Carvalho Chehab 21092886477fSRicardo Ribalda /* Add GPIO entity to the first chain. */ 21102886477fSRicardo Ribalda if (dev->gpio_unit) { 21112886477fSRicardo Ribalda chain = list_first_entry(&dev->chains, 21122886477fSRicardo Ribalda struct uvc_video_chain, list); 21132886477fSRicardo Ribalda list_add_tail(&dev->gpio_unit->chain, &chain->entities); 21142886477fSRicardo Ribalda } 21152886477fSRicardo Ribalda 21160c0d06caSMauro Carvalho Chehab return 0; 21170c0d06caSMauro Carvalho Chehab } 21180c0d06caSMauro Carvalho Chehab 21190c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 21200c0d06caSMauro Carvalho Chehab * Video device registration and unregistration 21210c0d06caSMauro Carvalho Chehab */ 21220c0d06caSMauro Carvalho Chehab 21230c0d06caSMauro Carvalho Chehab /* 21240c0d06caSMauro Carvalho Chehab * Delete the UVC device. 21250c0d06caSMauro Carvalho Chehab * 21260c0d06caSMauro Carvalho Chehab * Called by the kernel when the last reference to the uvc_device structure 21270c0d06caSMauro Carvalho Chehab * is released. 21280c0d06caSMauro Carvalho Chehab * 21290c0d06caSMauro Carvalho Chehab * As this function is called after or during disconnect(), all URBs have 2130ece41454SKieran Bingham * already been cancelled by the USB core. There is no need to kill the 21310c0d06caSMauro Carvalho Chehab * interrupt URB manually. 21320c0d06caSMauro Carvalho Chehab */ 21339d15cd95SGuennadi Liakhovetski static void uvc_delete(struct kref *kref) 21340c0d06caSMauro Carvalho Chehab { 21359d15cd95SGuennadi Liakhovetski struct uvc_device *dev = container_of(kref, struct uvc_device, ref); 21360c0d06caSMauro Carvalho Chehab struct list_head *p, *n; 21370c0d06caSMauro Carvalho Chehab 21380c0d06caSMauro Carvalho Chehab uvc_status_cleanup(dev); 21390c0d06caSMauro Carvalho Chehab uvc_ctrl_cleanup_device(dev); 21400c0d06caSMauro Carvalho Chehab 21412228d80dSTakashi Iwai usb_put_intf(dev->intf); 21422228d80dSTakashi Iwai usb_put_dev(dev->udev); 21432228d80dSTakashi Iwai 21440c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 21459832e155SJavier Martinez Canillas media_device_cleanup(&dev->mdev); 21460c0d06caSMauro Carvalho Chehab #endif 21470c0d06caSMauro Carvalho Chehab 21480c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->chains) { 21490c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 21500c0d06caSMauro Carvalho Chehab chain = list_entry(p, struct uvc_video_chain, list); 21510c0d06caSMauro Carvalho Chehab kfree(chain); 21520c0d06caSMauro Carvalho Chehab } 21530c0d06caSMauro Carvalho Chehab 21540c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->entities) { 21550c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 21560c0d06caSMauro Carvalho Chehab entity = list_entry(p, struct uvc_entity, list); 21570c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 21580c0d06caSMauro Carvalho Chehab uvc_mc_cleanup_entity(entity); 21590c0d06caSMauro Carvalho Chehab #endif 21600c0d06caSMauro Carvalho Chehab kfree(entity); 21610c0d06caSMauro Carvalho Chehab } 21620c0d06caSMauro Carvalho Chehab 21630c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->streams) { 21640c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming; 21650c0d06caSMauro Carvalho Chehab streaming = list_entry(p, struct uvc_streaming, list); 21660c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, 21670c0d06caSMauro Carvalho Chehab streaming->intf); 2168ece41454SKieran Bingham uvc_stream_delete(streaming); 21690c0d06caSMauro Carvalho Chehab } 21700c0d06caSMauro Carvalho Chehab 21710c0d06caSMauro Carvalho Chehab kfree(dev); 21720c0d06caSMauro Carvalho Chehab } 21730c0d06caSMauro Carvalho Chehab 21740c0d06caSMauro Carvalho Chehab static void uvc_release(struct video_device *vdev) 21750c0d06caSMauro Carvalho Chehab { 21760c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream = video_get_drvdata(vdev); 21770c0d06caSMauro Carvalho Chehab struct uvc_device *dev = stream->dev; 21780c0d06caSMauro Carvalho Chehab 21799d15cd95SGuennadi Liakhovetski kref_put(&dev->ref, uvc_delete); 21800c0d06caSMauro Carvalho Chehab } 21810c0d06caSMauro Carvalho Chehab 21820c0d06caSMauro Carvalho Chehab /* 21830c0d06caSMauro Carvalho Chehab * Unregister the video devices. 21840c0d06caSMauro Carvalho Chehab */ 21850c0d06caSMauro Carvalho Chehab static void uvc_unregister_video(struct uvc_device *dev) 21860c0d06caSMauro Carvalho Chehab { 21870c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 21880c0d06caSMauro Carvalho Chehab 21890c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 2190d8da7513SHans Verkuil if (!video_is_registered(&stream->vdev)) 21910c0d06caSMauro Carvalho Chehab continue; 21920c0d06caSMauro Carvalho Chehab 2193d8da7513SHans Verkuil video_unregister_device(&stream->vdev); 2194088ead25SGuennadi Liakhovetski video_unregister_device(&stream->meta.vdev); 21950c0d06caSMauro Carvalho Chehab 21960c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup_stream(stream); 21970c0d06caSMauro Carvalho Chehab } 219810e1fdb9SDaniel Axtens 219910e1fdb9SDaniel Axtens uvc_status_unregister(dev); 220010e1fdb9SDaniel Axtens 220110e1fdb9SDaniel Axtens if (dev->vdev.dev) 220210e1fdb9SDaniel Axtens v4l2_device_unregister(&dev->vdev); 220310e1fdb9SDaniel Axtens #ifdef CONFIG_MEDIA_CONTROLLER 220410e1fdb9SDaniel Axtens if (media_devnode_is_registered(dev->mdev.devnode)) 220510e1fdb9SDaniel Axtens media_device_unregister(&dev->mdev); 220610e1fdb9SDaniel Axtens #endif 22070c0d06caSMauro Carvalho Chehab } 22080c0d06caSMauro Carvalho Chehab 220931a96f4cSLaurent Pinchart int uvc_register_video_device(struct uvc_device *dev, 221031a96f4cSLaurent Pinchart struct uvc_streaming *stream, 221131a96f4cSLaurent Pinchart struct video_device *vdev, 221231a96f4cSLaurent Pinchart struct uvc_video_queue *queue, 221331a96f4cSLaurent Pinchart enum v4l2_buf_type type, 221431a96f4cSLaurent Pinchart const struct v4l2_file_operations *fops, 221531a96f4cSLaurent Pinchart const struct v4l2_ioctl_ops *ioctl_ops) 22160c0d06caSMauro Carvalho Chehab { 22170c0d06caSMauro Carvalho Chehab int ret; 22180c0d06caSMauro Carvalho Chehab 2219b83bba24SLaurent Pinchart /* Initialize the video buffers queue. */ 222031a96f4cSLaurent Pinchart ret = uvc_queue_init(queue, type, !uvc_no_drop_param); 2221b83bba24SLaurent Pinchart if (ret) 2222b83bba24SLaurent Pinchart return ret; 2223b83bba24SLaurent Pinchart 22240c0d06caSMauro Carvalho Chehab /* Register the device with V4L. */ 22250c0d06caSMauro Carvalho Chehab 222631a96f4cSLaurent Pinchart /* 222731a96f4cSLaurent Pinchart * We already hold a reference to dev->udev. The video device will be 22280c0d06caSMauro Carvalho Chehab * unregistered before the reference is released, so we don't need to 22290c0d06caSMauro Carvalho Chehab * get another one. 22300c0d06caSMauro Carvalho Chehab */ 22310c0d06caSMauro Carvalho Chehab vdev->v4l2_dev = &dev->vdev; 223231a96f4cSLaurent Pinchart vdev->fops = fops; 223331a96f4cSLaurent Pinchart vdev->ioctl_ops = ioctl_ops; 22340c0d06caSMauro Carvalho Chehab vdev->release = uvc_release; 22350550513cSLaurent Pinchart vdev->prio = &stream->chain->prio; 223631a96f4cSLaurent Pinchart if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 2237954f340fSHans Verkuil vdev->vfl_dir = VFL_DIR_TX; 223831a96f4cSLaurent Pinchart else 223931a96f4cSLaurent Pinchart vdev->vfl_dir = VFL_DIR_RX; 224094c53e26SLaurent Pinchart 224194c53e26SLaurent Pinchart switch (type) { 224294c53e26SLaurent Pinchart case V4L2_BUF_TYPE_VIDEO_CAPTURE: 224394c53e26SLaurent Pinchart default: 224494c53e26SLaurent Pinchart vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; 224594c53e26SLaurent Pinchart break; 224694c53e26SLaurent Pinchart case V4L2_BUF_TYPE_VIDEO_OUTPUT: 224794c53e26SLaurent Pinchart vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; 224894c53e26SLaurent Pinchart break; 2249088ead25SGuennadi Liakhovetski case V4L2_BUF_TYPE_META_CAPTURE: 2250088ead25SGuennadi Liakhovetski vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING; 2251088ead25SGuennadi Liakhovetski break; 225294c53e26SLaurent Pinchart } 225394c53e26SLaurent Pinchart 2254f66dcb32SRicardo Ribalda strscpy(vdev->name, dev->name, sizeof(vdev->name)); 22550c0d06caSMauro Carvalho Chehab 225631a96f4cSLaurent Pinchart /* 225731a96f4cSLaurent Pinchart * Set the driver data before calling video_register_device, otherwise 225831a96f4cSLaurent Pinchart * the file open() handler might race us. 22590c0d06caSMauro Carvalho Chehab */ 22600c0d06caSMauro Carvalho Chehab video_set_drvdata(vdev, stream); 22610c0d06caSMauro Carvalho Chehab 22627fbbbc78SHans Verkuil ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 22630c0d06caSMauro Carvalho Chehab if (ret < 0) { 226469df0954SRicardo Ribalda dev_err(&stream->intf->dev, 226569df0954SRicardo Ribalda "Failed to register %s device (%d).\n", 226631a96f4cSLaurent Pinchart v4l2_type_names[type], ret); 226731a96f4cSLaurent Pinchart return ret; 226831a96f4cSLaurent Pinchart } 226931a96f4cSLaurent Pinchart 227031a96f4cSLaurent Pinchart kref_get(&dev->ref); 227131a96f4cSLaurent Pinchart return 0; 227231a96f4cSLaurent Pinchart } 227331a96f4cSLaurent Pinchart 227431a96f4cSLaurent Pinchart static int uvc_register_video(struct uvc_device *dev, 227531a96f4cSLaurent Pinchart struct uvc_streaming *stream) 227631a96f4cSLaurent Pinchart { 227731a96f4cSLaurent Pinchart int ret; 227831a96f4cSLaurent Pinchart 227931a96f4cSLaurent Pinchart /* Initialize the streaming interface with default parameters. */ 228031a96f4cSLaurent Pinchart ret = uvc_video_init(stream); 228131a96f4cSLaurent Pinchart if (ret < 0) { 228269df0954SRicardo Ribalda dev_err(&stream->intf->dev, 228369df0954SRicardo Ribalda "Failed to initialize the device (%d).\n", ret); 22840c0d06caSMauro Carvalho Chehab return ret; 22850c0d06caSMauro Carvalho Chehab } 22860c0d06caSMauro Carvalho Chehab 2287f887e99aSLaurent Pinchart if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 2288088ead25SGuennadi Liakhovetski stream->chain->caps |= V4L2_CAP_VIDEO_CAPTURE 2289088ead25SGuennadi Liakhovetski | V4L2_CAP_META_CAPTURE; 2290f887e99aSLaurent Pinchart else 2291f887e99aSLaurent Pinchart stream->chain->caps |= V4L2_CAP_VIDEO_OUTPUT; 2292f887e99aSLaurent Pinchart 229331a96f4cSLaurent Pinchart uvc_debugfs_init_stream(stream); 229431a96f4cSLaurent Pinchart 229531a96f4cSLaurent Pinchart /* Register the device with V4L. */ 229631a96f4cSLaurent Pinchart return uvc_register_video_device(dev, stream, &stream->vdev, 229731a96f4cSLaurent Pinchart &stream->queue, stream->type, 229831a96f4cSLaurent Pinchart &uvc_fops, &uvc_ioctl_ops); 22990c0d06caSMauro Carvalho Chehab } 23000c0d06caSMauro Carvalho Chehab 23010c0d06caSMauro Carvalho Chehab /* 23020c0d06caSMauro Carvalho Chehab * Register all video devices in all chains. 23030c0d06caSMauro Carvalho Chehab */ 23040c0d06caSMauro Carvalho Chehab static int uvc_register_terms(struct uvc_device *dev, 23050c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain) 23060c0d06caSMauro Carvalho Chehab { 23070c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 23080c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 23090c0d06caSMauro Carvalho Chehab int ret; 23100c0d06caSMauro Carvalho Chehab 23110c0d06caSMauro Carvalho Chehab list_for_each_entry(term, &chain->entities, chain) { 23120c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) 23130c0d06caSMauro Carvalho Chehab continue; 23140c0d06caSMauro Carvalho Chehab 23150c0d06caSMauro Carvalho Chehab stream = uvc_stream_by_id(dev, term->id); 23160c0d06caSMauro Carvalho Chehab if (stream == NULL) { 231769df0954SRicardo Ribalda dev_info(&dev->udev->dev, 231869df0954SRicardo Ribalda "No streaming interface found for terminal %u.", 231969df0954SRicardo Ribalda term->id); 23200c0d06caSMauro Carvalho Chehab continue; 23210c0d06caSMauro Carvalho Chehab } 23220c0d06caSMauro Carvalho Chehab 23230c0d06caSMauro Carvalho Chehab stream->chain = chain; 23240c0d06caSMauro Carvalho Chehab ret = uvc_register_video(dev, stream); 23250c0d06caSMauro Carvalho Chehab if (ret < 0) 23260c0d06caSMauro Carvalho Chehab return ret; 23270c0d06caSMauro Carvalho Chehab 2328699b9a86SLaurent Pinchart /* 2329699b9a86SLaurent Pinchart * Register a metadata node, but ignore a possible failure, 2330088ead25SGuennadi Liakhovetski * complete registration of video nodes anyway. 2331088ead25SGuennadi Liakhovetski */ 2332088ead25SGuennadi Liakhovetski uvc_meta_register(stream); 2333088ead25SGuennadi Liakhovetski 2334d8da7513SHans Verkuil term->vdev = &stream->vdev; 23350c0d06caSMauro Carvalho Chehab } 23360c0d06caSMauro Carvalho Chehab 23370c0d06caSMauro Carvalho Chehab return 0; 23380c0d06caSMauro Carvalho Chehab } 23390c0d06caSMauro Carvalho Chehab 23400c0d06caSMauro Carvalho Chehab static int uvc_register_chains(struct uvc_device *dev) 23410c0d06caSMauro Carvalho Chehab { 23420c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 23430c0d06caSMauro Carvalho Chehab int ret; 23440c0d06caSMauro Carvalho Chehab 23450c0d06caSMauro Carvalho Chehab list_for_each_entry(chain, &dev->chains, list) { 23460c0d06caSMauro Carvalho Chehab ret = uvc_register_terms(dev, chain); 23470c0d06caSMauro Carvalho Chehab if (ret < 0) 23480c0d06caSMauro Carvalho Chehab return ret; 23490c0d06caSMauro Carvalho Chehab 23500c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 23510c0d06caSMauro Carvalho Chehab ret = uvc_mc_register_entities(chain); 23526689df06SColin Ian King if (ret < 0) 235369df0954SRicardo Ribalda dev_info(&dev->udev->dev, 23546689df06SColin Ian King "Failed to register entities (%d).\n", ret); 23550c0d06caSMauro Carvalho Chehab #endif 23560c0d06caSMauro Carvalho Chehab } 23570c0d06caSMauro Carvalho Chehab 23580c0d06caSMauro Carvalho Chehab return 0; 23590c0d06caSMauro Carvalho Chehab } 23600c0d06caSMauro Carvalho Chehab 23610c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 23620c0d06caSMauro Carvalho Chehab * USB probe, disconnect, suspend and resume 23630c0d06caSMauro Carvalho Chehab */ 23640c0d06caSMauro Carvalho Chehab 23653a03284dSLaurent Pinchart static const struct uvc_device_info uvc_quirk_none = { 0 }; 23663bc85817SGuennadi Liakhovetski 23670c0d06caSMauro Carvalho Chehab static int uvc_probe(struct usb_interface *intf, 23680c0d06caSMauro Carvalho Chehab const struct usb_device_id *id) 23690c0d06caSMauro Carvalho Chehab { 23700c0d06caSMauro Carvalho Chehab struct usb_device *udev = interface_to_usbdev(intf); 23710c0d06caSMauro Carvalho Chehab struct uvc_device *dev; 23723bc85817SGuennadi Liakhovetski const struct uvc_device_info *info = 23733bc85817SGuennadi Liakhovetski (const struct uvc_device_info *)id->driver_info; 2374e7b09f18SPeter Boström int function; 23750c0d06caSMauro Carvalho Chehab int ret; 23760c0d06caSMauro Carvalho Chehab 23770c0d06caSMauro Carvalho Chehab /* Allocate memory for the device and initialize it. */ 2378f14d4988SLaurent Pinchart dev = kzalloc(sizeof(*dev), GFP_KERNEL); 2379f14d4988SLaurent Pinchart if (dev == NULL) 23800c0d06caSMauro Carvalho Chehab return -ENOMEM; 23810c0d06caSMauro Carvalho Chehab 23820c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->entities); 23830c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->chains); 23840c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->streams); 23859d15cd95SGuennadi Liakhovetski kref_init(&dev->ref); 23860c0d06caSMauro Carvalho Chehab atomic_set(&dev->nmappings, 0); 238717706f56SLaurent Pinchart mutex_init(&dev->lock); 23880c0d06caSMauro Carvalho Chehab 23890c0d06caSMauro Carvalho Chehab dev->udev = usb_get_dev(udev); 23900c0d06caSMauro Carvalho Chehab dev->intf = usb_get_intf(intf); 23910c0d06caSMauro Carvalho Chehab dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; 23923a03284dSLaurent Pinchart dev->info = info ? info : &uvc_quirk_none; 23933a03284dSLaurent Pinchart dev->quirks = uvc_quirks_param == -1 23943a03284dSLaurent Pinchart ? dev->info->quirks : uvc_quirks_param; 23950c0d06caSMauro Carvalho Chehab 2396ed4c5fa4SRicardo Ribalda if (id->idVendor && id->idProduct) 23979e56380aSJoe Perches uvc_dbg(dev, PROBE, "Probing known UVC device %s (%04x:%04x)\n", 2398ed4c5fa4SRicardo Ribalda udev->devpath, id->idVendor, id->idProduct); 2399ed4c5fa4SRicardo Ribalda else 24009e56380aSJoe Perches uvc_dbg(dev, PROBE, "Probing generic UVC device %s\n", 24019e56380aSJoe Perches udev->devpath); 2402ed4c5fa4SRicardo Ribalda 24030c0d06caSMauro Carvalho Chehab if (udev->product != NULL) 2404c0decac1SMauro Carvalho Chehab strscpy(dev->name, udev->product, sizeof(dev->name)); 24050c0d06caSMauro Carvalho Chehab else 2406f14d4988SLaurent Pinchart snprintf(dev->name, sizeof(dev->name), 24070c0d06caSMauro Carvalho Chehab "UVC Camera (%04x:%04x)", 24080c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idVendor), 24090c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idProduct)); 24100c0d06caSMauro Carvalho Chehab 2411e7b09f18SPeter Boström /* 2412e7b09f18SPeter Boström * Add iFunction or iInterface to names when available as additional 2413e7b09f18SPeter Boström * distinguishers between interfaces. iFunction is prioritized over 2414e7b09f18SPeter Boström * iInterface which matches Windows behavior at the point of writing. 2415e7b09f18SPeter Boström */ 2416e7b09f18SPeter Boström if (intf->intf_assoc && intf->intf_assoc->iFunction != 0) 2417e7b09f18SPeter Boström function = intf->intf_assoc->iFunction; 2418e7b09f18SPeter Boström else 2419e7b09f18SPeter Boström function = intf->cur_altsetting->desc.iInterface; 2420e7b09f18SPeter Boström if (function != 0) { 2421e7b09f18SPeter Boström size_t len; 2422e7b09f18SPeter Boström 2423e7b09f18SPeter Boström strlcat(dev->name, ": ", sizeof(dev->name)); 2424e7b09f18SPeter Boström len = strlen(dev->name); 2425e7b09f18SPeter Boström usb_string(udev, function, dev->name + len, 2426e7b09f18SPeter Boström sizeof(dev->name) - len); 2427e7b09f18SPeter Boström } 2428e7b09f18SPeter Boström 24298c279e93SLaurent Pinchart /* Initialize the media device. */ 24308c279e93SLaurent Pinchart #ifdef CONFIG_MEDIA_CONTROLLER 24318c279e93SLaurent Pinchart dev->mdev.dev = &intf->dev; 24328c279e93SLaurent Pinchart strscpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model)); 24338c279e93SLaurent Pinchart if (udev->serial) 24348c279e93SLaurent Pinchart strscpy(dev->mdev.serial, udev->serial, 24358c279e93SLaurent Pinchart sizeof(dev->mdev.serial)); 24368c279e93SLaurent Pinchart usb_make_path(udev, dev->mdev.bus_info, sizeof(dev->mdev.bus_info)); 24378c279e93SLaurent Pinchart dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); 24388c279e93SLaurent Pinchart media_device_init(&dev->mdev); 24398c279e93SLaurent Pinchart 24408c279e93SLaurent Pinchart dev->vdev.mdev = &dev->mdev; 24418c279e93SLaurent Pinchart #endif 24428c279e93SLaurent Pinchart 24430c0d06caSMauro Carvalho Chehab /* Parse the Video Class control descriptor. */ 24440c0d06caSMauro Carvalho Chehab if (uvc_parse_control(dev) < 0) { 24459e56380aSJoe Perches uvc_dbg(dev, PROBE, "Unable to parse UVC descriptors\n"); 24460c0d06caSMauro Carvalho Chehab goto error; 24470c0d06caSMauro Carvalho Chehab } 24480c0d06caSMauro Carvalho Chehab 24492886477fSRicardo Ribalda /* Parse the associated GPIOs. */ 24502886477fSRicardo Ribalda if (uvc_gpio_parse(dev) < 0) { 24519e56380aSJoe Perches uvc_dbg(dev, PROBE, "Unable to parse UVC GPIOs\n"); 24522886477fSRicardo Ribalda goto error; 24532886477fSRicardo Ribalda } 24542886477fSRicardo Ribalda 245569df0954SRicardo Ribalda dev_info(&dev->udev->dev, "Found UVC %u.%02x device %s (%04x:%04x)\n", 24560c0d06caSMauro Carvalho Chehab dev->uvc_version >> 8, dev->uvc_version & 0xff, 24570c0d06caSMauro Carvalho Chehab udev->product ? udev->product : "<unnamed>", 24580c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idVendor), 24590c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idProduct)); 24600c0d06caSMauro Carvalho Chehab 24613a03284dSLaurent Pinchart if (dev->quirks != dev->info->quirks) { 246269df0954SRicardo Ribalda dev_info(&dev->udev->dev, 246369df0954SRicardo Ribalda "Forcing device quirks to 0x%x by module parameter for testing purpose.\n", 246469df0954SRicardo Ribalda dev->quirks); 246569df0954SRicardo Ribalda dev_info(&dev->udev->dev, 2466e3a0f556SJonathan Neuschäfer "Please report required quirks to the linux-media mailing list.\n"); 24670c0d06caSMauro Carvalho Chehab } 24680c0d06caSMauro Carvalho Chehab 2469b400b6f2SLaurent Pinchart if (dev->info->uvc_version) { 2470b400b6f2SLaurent Pinchart dev->uvc_version = dev->info->uvc_version; 247169df0954SRicardo Ribalda dev_info(&dev->udev->dev, "Forcing UVC version to %u.%02x\n", 2472b400b6f2SLaurent Pinchart dev->uvc_version >> 8, dev->uvc_version & 0xff); 2473b400b6f2SLaurent Pinchart } 2474b400b6f2SLaurent Pinchart 24758c279e93SLaurent Pinchart /* Register the V4L2 device. */ 24760c0d06caSMauro Carvalho Chehab if (v4l2_device_register(&intf->dev, &dev->vdev) < 0) 24770c0d06caSMauro Carvalho Chehab goto error; 24780c0d06caSMauro Carvalho Chehab 24790c0d06caSMauro Carvalho Chehab /* Scan the device for video chains. */ 24800c0d06caSMauro Carvalho Chehab if (uvc_scan_device(dev) < 0) 24810c0d06caSMauro Carvalho Chehab goto error; 24820c0d06caSMauro Carvalho Chehab 2483866c6bddSRicardo Ribalda /* Initialize controls. */ 2484866c6bddSRicardo Ribalda if (uvc_ctrl_init_device(dev) < 0) 2485866c6bddSRicardo Ribalda goto error; 2486866c6bddSRicardo Ribalda 24870c0d06caSMauro Carvalho Chehab /* Register video device nodes. */ 24880c0d06caSMauro Carvalho Chehab if (uvc_register_chains(dev) < 0) 24890c0d06caSMauro Carvalho Chehab goto error; 24900c0d06caSMauro Carvalho Chehab 24919832e155SJavier Martinez Canillas #ifdef CONFIG_MEDIA_CONTROLLER 24929832e155SJavier Martinez Canillas /* Register the media device node */ 24939832e155SJavier Martinez Canillas if (media_device_register(&dev->mdev) < 0) 24949832e155SJavier Martinez Canillas goto error; 24959832e155SJavier Martinez Canillas #endif 24960c0d06caSMauro Carvalho Chehab /* Save our data pointer in the interface data. */ 24970c0d06caSMauro Carvalho Chehab usb_set_intfdata(intf, dev); 24980c0d06caSMauro Carvalho Chehab 24990c0d06caSMauro Carvalho Chehab /* Initialize the interrupt URB. */ 25000c0d06caSMauro Carvalho Chehab if ((ret = uvc_status_init(dev)) < 0) { 250169df0954SRicardo Ribalda dev_info(&dev->udev->dev, 250269df0954SRicardo Ribalda "Unable to initialize the status endpoint (%d), status interrupt will not be supported.\n", 250369df0954SRicardo Ribalda ret); 25040c0d06caSMauro Carvalho Chehab } 25050c0d06caSMauro Carvalho Chehab 25062886477fSRicardo Ribalda ret = uvc_gpio_init_irq(dev); 25072886477fSRicardo Ribalda if (ret < 0) { 25082886477fSRicardo Ribalda dev_err(&dev->udev->dev, 25092886477fSRicardo Ribalda "Unable to request privacy GPIO IRQ (%d)\n", ret); 25102886477fSRicardo Ribalda goto error; 25112886477fSRicardo Ribalda } 25122886477fSRicardo Ribalda 25139e56380aSJoe Perches uvc_dbg(dev, PROBE, "UVC device initialized\n"); 25140c0d06caSMauro Carvalho Chehab usb_enable_autosuspend(udev); 25150c0d06caSMauro Carvalho Chehab return 0; 25160c0d06caSMauro Carvalho Chehab 25170c0d06caSMauro Carvalho Chehab error: 25180c0d06caSMauro Carvalho Chehab uvc_unregister_video(dev); 2519f9ffcb0aSPhilipp Zabel kref_put(&dev->ref, uvc_delete); 25200c0d06caSMauro Carvalho Chehab return -ENODEV; 25210c0d06caSMauro Carvalho Chehab } 25220c0d06caSMauro Carvalho Chehab 25230c0d06caSMauro Carvalho Chehab static void uvc_disconnect(struct usb_interface *intf) 25240c0d06caSMauro Carvalho Chehab { 25250c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 25260c0d06caSMauro Carvalho Chehab 2527699b9a86SLaurent Pinchart /* 2528699b9a86SLaurent Pinchart * Set the USB interface data to NULL. This can be done outside the 25290c0d06caSMauro Carvalho Chehab * lock, as there's no other reader. 25300c0d06caSMauro Carvalho Chehab */ 25310c0d06caSMauro Carvalho Chehab usb_set_intfdata(intf, NULL); 25320c0d06caSMauro Carvalho Chehab 25330c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 25340c0d06caSMauro Carvalho Chehab UVC_SC_VIDEOSTREAMING) 25350c0d06caSMauro Carvalho Chehab return; 25360c0d06caSMauro Carvalho Chehab 25370c0d06caSMauro Carvalho Chehab uvc_unregister_video(dev); 2538f9ffcb0aSPhilipp Zabel kref_put(&dev->ref, uvc_delete); 25390c0d06caSMauro Carvalho Chehab } 25400c0d06caSMauro Carvalho Chehab 25410c0d06caSMauro Carvalho Chehab static int uvc_suspend(struct usb_interface *intf, pm_message_t message) 25420c0d06caSMauro Carvalho Chehab { 25430c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 25440c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 25450c0d06caSMauro Carvalho Chehab 25469e56380aSJoe Perches uvc_dbg(dev, SUSPEND, "Suspending interface %u\n", 25470c0d06caSMauro Carvalho Chehab intf->cur_altsetting->desc.bInterfaceNumber); 25480c0d06caSMauro Carvalho Chehab 25490c0d06caSMauro Carvalho Chehab /* Controls are cached on the fly so they don't need to be saved. */ 25500c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 255117706f56SLaurent Pinchart UVC_SC_VIDEOCONTROL) { 255217706f56SLaurent Pinchart mutex_lock(&dev->lock); 255317706f56SLaurent Pinchart if (dev->users) 255417706f56SLaurent Pinchart uvc_status_stop(dev); 255517706f56SLaurent Pinchart mutex_unlock(&dev->lock); 255617706f56SLaurent Pinchart return 0; 255717706f56SLaurent Pinchart } 25580c0d06caSMauro Carvalho Chehab 25590c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 25600c0d06caSMauro Carvalho Chehab if (stream->intf == intf) 25610c0d06caSMauro Carvalho Chehab return uvc_video_suspend(stream); 25620c0d06caSMauro Carvalho Chehab } 25630c0d06caSMauro Carvalho Chehab 25649e56380aSJoe Perches uvc_dbg(dev, SUSPEND, 25659e56380aSJoe Perches "Suspend: video streaming USB interface mismatch\n"); 25660c0d06caSMauro Carvalho Chehab return -EINVAL; 25670c0d06caSMauro Carvalho Chehab } 25680c0d06caSMauro Carvalho Chehab 25690c0d06caSMauro Carvalho Chehab static int __uvc_resume(struct usb_interface *intf, int reset) 25700c0d06caSMauro Carvalho Chehab { 25710c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 25720c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 2573b83bba24SLaurent Pinchart int ret = 0; 25740c0d06caSMauro Carvalho Chehab 25759e56380aSJoe Perches uvc_dbg(dev, SUSPEND, "Resuming interface %u\n", 25760c0d06caSMauro Carvalho Chehab intf->cur_altsetting->desc.bInterfaceNumber); 25770c0d06caSMauro Carvalho Chehab 25780c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 25790c0d06caSMauro Carvalho Chehab UVC_SC_VIDEOCONTROL) { 258017706f56SLaurent Pinchart if (reset) { 258117e1319fSWilliam Manley ret = uvc_ctrl_restore_values(dev); 25820c0d06caSMauro Carvalho Chehab if (ret < 0) 25830c0d06caSMauro Carvalho Chehab return ret; 25840c0d06caSMauro Carvalho Chehab } 25850c0d06caSMauro Carvalho Chehab 258617706f56SLaurent Pinchart mutex_lock(&dev->lock); 258717706f56SLaurent Pinchart if (dev->users) 258817706f56SLaurent Pinchart ret = uvc_status_start(dev, GFP_NOIO); 258917706f56SLaurent Pinchart mutex_unlock(&dev->lock); 259017706f56SLaurent Pinchart 259117706f56SLaurent Pinchart return ret; 25920c0d06caSMauro Carvalho Chehab } 25930c0d06caSMauro Carvalho Chehab 25940c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 2595b83bba24SLaurent Pinchart if (stream->intf == intf) { 2596b83bba24SLaurent Pinchart ret = uvc_video_resume(stream, reset); 2597b83bba24SLaurent Pinchart if (ret < 0) 25980da4ab98SLaurent Pinchart uvc_queue_streamoff(&stream->queue, 25990da4ab98SLaurent Pinchart stream->queue.queue.type); 2600b83bba24SLaurent Pinchart return ret; 2601b83bba24SLaurent Pinchart } 26020c0d06caSMauro Carvalho Chehab } 26030c0d06caSMauro Carvalho Chehab 26049e56380aSJoe Perches uvc_dbg(dev, SUSPEND, 26059e56380aSJoe Perches "Resume: video streaming USB interface mismatch\n"); 26060c0d06caSMauro Carvalho Chehab return -EINVAL; 26070c0d06caSMauro Carvalho Chehab } 26080c0d06caSMauro Carvalho Chehab 26090c0d06caSMauro Carvalho Chehab static int uvc_resume(struct usb_interface *intf) 26100c0d06caSMauro Carvalho Chehab { 26110c0d06caSMauro Carvalho Chehab return __uvc_resume(intf, 0); 26120c0d06caSMauro Carvalho Chehab } 26130c0d06caSMauro Carvalho Chehab 26140c0d06caSMauro Carvalho Chehab static int uvc_reset_resume(struct usb_interface *intf) 26150c0d06caSMauro Carvalho Chehab { 26160c0d06caSMauro Carvalho Chehab return __uvc_resume(intf, 1); 26170c0d06caSMauro Carvalho Chehab } 26180c0d06caSMauro Carvalho Chehab 26190c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 26200c0d06caSMauro Carvalho Chehab * Module parameters 26210c0d06caSMauro Carvalho Chehab */ 26220c0d06caSMauro Carvalho Chehab 2623e4dca7b7SKees Cook static int uvc_clock_param_get(char *buffer, const struct kernel_param *kp) 26240c0d06caSMauro Carvalho Chehab { 26250c0d06caSMauro Carvalho Chehab if (uvc_clock_param == CLOCK_MONOTONIC) 26260c0d06caSMauro Carvalho Chehab return sprintf(buffer, "CLOCK_MONOTONIC"); 26270c0d06caSMauro Carvalho Chehab else 26280c0d06caSMauro Carvalho Chehab return sprintf(buffer, "CLOCK_REALTIME"); 26290c0d06caSMauro Carvalho Chehab } 26300c0d06caSMauro Carvalho Chehab 2631e4dca7b7SKees Cook static int uvc_clock_param_set(const char *val, const struct kernel_param *kp) 26320c0d06caSMauro Carvalho Chehab { 26330c0d06caSMauro Carvalho Chehab if (strncasecmp(val, "clock_", strlen("clock_")) == 0) 26340c0d06caSMauro Carvalho Chehab val += strlen("clock_"); 26350c0d06caSMauro Carvalho Chehab 26360c0d06caSMauro Carvalho Chehab if (strcasecmp(val, "monotonic") == 0) 26370c0d06caSMauro Carvalho Chehab uvc_clock_param = CLOCK_MONOTONIC; 26380c0d06caSMauro Carvalho Chehab else if (strcasecmp(val, "realtime") == 0) 26390c0d06caSMauro Carvalho Chehab uvc_clock_param = CLOCK_REALTIME; 26400c0d06caSMauro Carvalho Chehab else 26410c0d06caSMauro Carvalho Chehab return -EINVAL; 26420c0d06caSMauro Carvalho Chehab 26430c0d06caSMauro Carvalho Chehab return 0; 26440c0d06caSMauro Carvalho Chehab } 26450c0d06caSMauro Carvalho Chehab 26460c0d06caSMauro Carvalho Chehab module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get, 26470c0d06caSMauro Carvalho Chehab &uvc_clock_param, S_IRUGO|S_IWUSR); 26480c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(clock, "Video buffers timestamp clock"); 26495d0fd3c8SLaurent Pinchart module_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, S_IRUGO|S_IWUSR); 26505d0fd3c8SLaurent Pinchart MODULE_PARM_DESC(hwtimestamps, "Use hardware timestamps"); 26510c0d06caSMauro Carvalho Chehab module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR); 26520c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); 26530c0d06caSMauro Carvalho Chehab module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); 26540c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(quirks, "Forced device quirks"); 26559e56380aSJoe Perches module_param_named(trace, uvc_dbg_param, uint, S_IRUGO|S_IWUSR); 26560c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(trace, "Trace level bitmask"); 26570c0d06caSMauro Carvalho Chehab module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR); 26580c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); 26590c0d06caSMauro Carvalho Chehab 26600c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 26610c0d06caSMauro Carvalho Chehab * Driver initialization and cleanup 26620c0d06caSMauro Carvalho Chehab */ 26630c0d06caSMauro Carvalho Chehab 266438207560SRicardo Ribalda static const struct uvc_menu_info power_line_frequency_controls_limited[] = { 266538207560SRicardo Ribalda { 1, "50 Hz" }, 266638207560SRicardo Ribalda { 2, "60 Hz" }, 266738207560SRicardo Ribalda }; 266838207560SRicardo Ribalda 266938207560SRicardo Ribalda static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = { 267038207560SRicardo Ribalda .id = V4L2_CID_POWER_LINE_FREQUENCY, 267138207560SRicardo Ribalda .entity = UVC_GUID_UVC_PROCESSING, 267238207560SRicardo Ribalda .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, 267338207560SRicardo Ribalda .size = 2, 267438207560SRicardo Ribalda .offset = 0, 267538207560SRicardo Ribalda .v4l2_type = V4L2_CTRL_TYPE_MENU, 267638207560SRicardo Ribalda .data_type = UVC_CTRL_DATA_TYPE_ENUM, 267738207560SRicardo Ribalda .menu_info = power_line_frequency_controls_limited, 267838207560SRicardo Ribalda .menu_count = ARRAY_SIZE(power_line_frequency_controls_limited), 267938207560SRicardo Ribalda }; 268038207560SRicardo Ribalda 268138207560SRicardo Ribalda static const struct uvc_device_info uvc_ctrl_power_line_limited = { 268238207560SRicardo Ribalda .mappings = (const struct uvc_control_mapping *[]) { 268338207560SRicardo Ribalda &uvc_ctrl_power_line_mapping_limited, 268438207560SRicardo Ribalda NULL, /* Sentinel */ 268538207560SRicardo Ribalda }, 268638207560SRicardo Ribalda }; 268738207560SRicardo Ribalda 26883bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_probe_minmax = { 26893bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_PROBE_MINMAX, 26903bc85817SGuennadi Liakhovetski }; 26913bc85817SGuennadi Liakhovetski 26923bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_fix_bandwidth = { 26933bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_FIX_BANDWIDTH, 26943bc85817SGuennadi Liakhovetski }; 26953bc85817SGuennadi Liakhovetski 26963bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_probe_def = { 26973bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_PROBE_DEF, 26983bc85817SGuennadi Liakhovetski }; 26993bc85817SGuennadi Liakhovetski 27003bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_stream_no_fid = { 27013bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_STREAM_NO_FID, 27023bc85817SGuennadi Liakhovetski }; 27033bc85817SGuennadi Liakhovetski 27043bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_force_y8 = { 27053bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_FORCE_Y8, 27063bc85817SGuennadi Liakhovetski }; 27073bc85817SGuennadi Liakhovetski 270888d8034cSGuennadi Liakhovetski #define UVC_INFO_QUIRK(q) (kernel_ulong_t)&(struct uvc_device_info){.quirks = q} 27096ea0d588SGuennadi Liakhovetski #define UVC_INFO_META(m) (kernel_ulong_t)&(struct uvc_device_info) \ 27106ea0d588SGuennadi Liakhovetski {.meta_format = m} 27113bc85817SGuennadi Liakhovetski 27120c0d06caSMauro Carvalho Chehab /* 27130c0d06caSMauro Carvalho Chehab * The Logitech cameras listed below have their interface class set to 27140c0d06caSMauro Carvalho Chehab * VENDOR_SPEC because they don't announce themselves as UVC devices, even 27150c0d06caSMauro Carvalho Chehab * though they are compliant. 27160c0d06caSMauro Carvalho Chehab */ 27177fb2e072SArvind Yadav static const struct usb_device_id uvc_ids[] = { 271838207560SRicardo Ribalda /* Quanta USB2.0 HD UVC Webcam */ 271938207560SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 272038207560SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 272138207560SRicardo Ribalda .idVendor = 0x0408, 272238207560SRicardo Ribalda .idProduct = 0x3090, 272338207560SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 272438207560SRicardo Ribalda .bInterfaceSubClass = 1, 272538207560SRicardo Ribalda .bInterfaceProtocol = 0, 272638207560SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 272795f03d97SRicardo Ribalda /* Quanta USB2.0 HD UVC Webcam */ 272895f03d97SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 272995f03d97SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 273095f03d97SRicardo Ribalda .idVendor = 0x0408, 273195f03d97SRicardo Ribalda .idProduct = 0x4030, 273295f03d97SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 273395f03d97SRicardo Ribalda .bInterfaceSubClass = 1, 273495f03d97SRicardo Ribalda .bInterfaceProtocol = 0, 273595f03d97SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 273695f03d97SRicardo Ribalda /* Quanta USB2.0 HD UVC Webcam */ 273795f03d97SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 273895f03d97SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 273995f03d97SRicardo Ribalda .idVendor = 0x0408, 274095f03d97SRicardo Ribalda .idProduct = 0x4034, 274195f03d97SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 274295f03d97SRicardo Ribalda .bInterfaceSubClass = 1, 274395f03d97SRicardo Ribalda .bInterfaceProtocol = 0, 274495f03d97SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 27450c0d06caSMauro Carvalho Chehab /* LogiLink Wireless Webcam */ 27460c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27470c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27480c0d06caSMauro Carvalho Chehab .idVendor = 0x0416, 27490c0d06caSMauro Carvalho Chehab .idProduct = 0xa91a, 27500c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27510c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27520c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27533bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 27540c0d06caSMauro Carvalho Chehab /* Genius eFace 2025 */ 27550c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27560c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27570c0d06caSMauro Carvalho Chehab .idVendor = 0x0458, 27580c0d06caSMauro Carvalho Chehab .idProduct = 0x706e, 27590c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27600c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27610c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27623bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 27630c0d06caSMauro Carvalho Chehab /* Microsoft Lifecam NX-6000 */ 27640c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27650c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27660c0d06caSMauro Carvalho Chehab .idVendor = 0x045e, 27670c0d06caSMauro Carvalho Chehab .idProduct = 0x00f8, 27680c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27690c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27700c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27713bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 27721558ec83SLaurent Pinchart /* Microsoft Lifecam NX-3000 */ 27731558ec83SLaurent Pinchart { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27741558ec83SLaurent Pinchart | USB_DEVICE_ID_MATCH_INT_INFO, 27751558ec83SLaurent Pinchart .idVendor = 0x045e, 27761558ec83SLaurent Pinchart .idProduct = 0x0721, 27771558ec83SLaurent Pinchart .bInterfaceClass = USB_CLASS_VIDEO, 27781558ec83SLaurent Pinchart .bInterfaceSubClass = 1, 27791558ec83SLaurent Pinchart .bInterfaceProtocol = 0, 27803bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 27810c0d06caSMauro Carvalho Chehab /* Microsoft Lifecam VX-7000 */ 27820c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27830c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27840c0d06caSMauro Carvalho Chehab .idVendor = 0x045e, 27850c0d06caSMauro Carvalho Chehab .idProduct = 0x0723, 27860c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27870c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27880c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27893bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 27900c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Fusion */ 27910c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27920c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27930c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 27940c0d06caSMauro Carvalho Chehab .idProduct = 0x08c1, 27950c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 27960c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27970c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 27980c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Orbit MP */ 27990c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28000c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28010c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 28020c0d06caSMauro Carvalho Chehab .idProduct = 0x08c2, 28030c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 28040c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28050c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 28060c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Pro for Notebook */ 28070c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28080c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28090c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 28100c0d06caSMauro Carvalho Chehab .idProduct = 0x08c3, 28110c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 28120c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28130c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 28140c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Pro 5000 */ 28150c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28160c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28170c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 28180c0d06caSMauro Carvalho Chehab .idProduct = 0x08c5, 28190c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 28200c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28210c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 28220c0d06caSMauro Carvalho Chehab /* Logitech Quickcam OEM Dell Notebook */ 28230c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28240c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28250c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 28260c0d06caSMauro Carvalho Chehab .idProduct = 0x08c6, 28270c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 28280c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28290c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 28300c0d06caSMauro Carvalho Chehab /* Logitech Quickcam OEM Cisco VT Camera II */ 28310c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28320c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28330c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 28340c0d06caSMauro Carvalho Chehab .idProduct = 0x08c7, 28350c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 28360c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28370c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 283817e1319fSWilliam Manley /* Logitech HD Pro Webcam C920 */ 283917e1319fSWilliam Manley { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 284017e1319fSWilliam Manley | USB_DEVICE_ID_MATCH_INT_INFO, 284117e1319fSWilliam Manley .idVendor = 0x046d, 284217e1319fSWilliam Manley .idProduct = 0x082d, 284317e1319fSWilliam Manley .bInterfaceClass = USB_CLASS_VIDEO, 284417e1319fSWilliam Manley .bInterfaceSubClass = 1, 284517e1319fSWilliam Manley .bInterfaceProtocol = 0, 284688d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT) }, 28470c0d06caSMauro Carvalho Chehab /* Chicony CNF7129 (Asus EEE 100HE) */ 28480c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28490c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28500c0d06caSMauro Carvalho Chehab .idVendor = 0x04f2, 28510c0d06caSMauro Carvalho Chehab .idProduct = 0xb071, 28520c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28530c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28540c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 285588d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTRICT_FRAME_RATE) }, 28569f22f959SRicardo Ribalda /* Chicony EasyCamera */ 28579f22f959SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28589f22f959SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 28599f22f959SRicardo Ribalda .idVendor = 0x04f2, 2860332a2235SRicardo Ribalda .idProduct = 0xb5eb, 2861332a2235SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 2862332a2235SRicardo Ribalda .bInterfaceSubClass = 1, 2863332a2235SRicardo Ribalda .bInterfaceProtocol = 0, 2864332a2235SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 2865332a2235SRicardo Ribalda /* Chicony EasyCamera */ 2866332a2235SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2867332a2235SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 2868332a2235SRicardo Ribalda .idVendor = 0x04f2, 28699f22f959SRicardo Ribalda .idProduct = 0xb6ba, 28709f22f959SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 28719f22f959SRicardo Ribalda .bInterfaceSubClass = 1, 28729f22f959SRicardo Ribalda .bInterfaceProtocol = 0, 28739f22f959SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 28749f22f959SRicardo Ribalda /* Chicony EasyCamera */ 28759f22f959SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28769f22f959SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 28779f22f959SRicardo Ribalda .idVendor = 0x04f2, 28789f22f959SRicardo Ribalda .idProduct = 0xb746, 28799f22f959SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 28809f22f959SRicardo Ribalda .bInterfaceSubClass = 1, 28819f22f959SRicardo Ribalda .bInterfaceProtocol = 0, 28829f22f959SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 28830c0d06caSMauro Carvalho Chehab /* Alcor Micro AU3820 (Future Boy PC USB Webcam) */ 28840c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28850c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28860c0d06caSMauro Carvalho Chehab .idVendor = 0x058f, 28870c0d06caSMauro Carvalho Chehab .idProduct = 0x3820, 28880c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28890c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28900c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 28913bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 28920c0d06caSMauro Carvalho Chehab /* Dell XPS m1530 */ 28930c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28940c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28950c0d06caSMauro Carvalho Chehab .idVendor = 0x05a9, 28960c0d06caSMauro Carvalho Chehab .idProduct = 0x2640, 28970c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28980c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28990c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 29003bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 290189e0f248SJoseph Salisbury /* Dell SP2008WFP Monitor */ 290289e0f248SJoseph Salisbury { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 290389e0f248SJoseph Salisbury | USB_DEVICE_ID_MATCH_INT_INFO, 290489e0f248SJoseph Salisbury .idVendor = 0x05a9, 290589e0f248SJoseph Salisbury .idProduct = 0x2641, 290689e0f248SJoseph Salisbury .bInterfaceClass = USB_CLASS_VIDEO, 290789e0f248SJoseph Salisbury .bInterfaceSubClass = 1, 290889e0f248SJoseph Salisbury .bInterfaceProtocol = 0, 29093bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 2910c2a273b2SJoseph Salisbury /* Dell Alienware X51 */ 2911c2a273b2SJoseph Salisbury { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2912c2a273b2SJoseph Salisbury | USB_DEVICE_ID_MATCH_INT_INFO, 2913c2a273b2SJoseph Salisbury .idVendor = 0x05a9, 2914c2a273b2SJoseph Salisbury .idProduct = 0x2643, 2915c2a273b2SJoseph Salisbury .bInterfaceClass = USB_CLASS_VIDEO, 2916c2a273b2SJoseph Salisbury .bInterfaceSubClass = 1, 2917c2a273b2SJoseph Salisbury .bInterfaceProtocol = 0, 29183bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 2919afcf44c7SKamal Mostafa /* Dell Studio Hybrid 140g (OmniVision webcam) */ 2920afcf44c7SKamal Mostafa { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2921afcf44c7SKamal Mostafa | USB_DEVICE_ID_MATCH_INT_INFO, 2922afcf44c7SKamal Mostafa .idVendor = 0x05a9, 2923afcf44c7SKamal Mostafa .idProduct = 0x264a, 2924afcf44c7SKamal Mostafa .bInterfaceClass = USB_CLASS_VIDEO, 2925afcf44c7SKamal Mostafa .bInterfaceSubClass = 1, 2926afcf44c7SKamal Mostafa .bInterfaceProtocol = 0, 29273bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 292862ea864fSPaul Fertser /* Dell XPS M1330 (OmniVision OV7670 webcam) */ 292962ea864fSPaul Fertser { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 293062ea864fSPaul Fertser | USB_DEVICE_ID_MATCH_INT_INFO, 293162ea864fSPaul Fertser .idVendor = 0x05a9, 293262ea864fSPaul Fertser .idProduct = 0x7670, 293362ea864fSPaul Fertser .bInterfaceClass = USB_CLASS_VIDEO, 293462ea864fSPaul Fertser .bInterfaceSubClass = 1, 293562ea864fSPaul Fertser .bInterfaceProtocol = 0, 29363bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 29370c0d06caSMauro Carvalho Chehab /* Apple Built-In iSight */ 29380c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29390c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 29400c0d06caSMauro Carvalho Chehab .idVendor = 0x05ac, 29410c0d06caSMauro Carvalho Chehab .idProduct = 0x8501, 29420c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 29430c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 29440c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 294588d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 29463bc85817SGuennadi Liakhovetski | UVC_QUIRK_BUILTIN_ISIGHT) }, 294753c26454SPaul Pawlowski /* Apple FaceTime HD Camera (Built-In) */ 294853c26454SPaul Pawlowski { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 294953c26454SPaul Pawlowski | USB_DEVICE_ID_MATCH_INT_INFO, 295053c26454SPaul Pawlowski .idVendor = 0x05ac, 295153c26454SPaul Pawlowski .idProduct = 0x8514, 295253c26454SPaul Pawlowski .bInterfaceClass = USB_CLASS_VIDEO, 295353c26454SPaul Pawlowski .bInterfaceSubClass = 1, 295453c26454SPaul Pawlowski .bInterfaceProtocol = 0, 295553c26454SPaul Pawlowski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 29567b848ed6SDaniel Roschka /* Apple Built-In iSight via iBridge */ 29577b848ed6SDaniel Roschka { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29587b848ed6SDaniel Roschka | USB_DEVICE_ID_MATCH_INT_INFO, 29597b848ed6SDaniel Roschka .idVendor = 0x05ac, 29607b848ed6SDaniel Roschka .idProduct = 0x8600, 29617b848ed6SDaniel Roschka .bInterfaceClass = USB_CLASS_VIDEO, 29627b848ed6SDaniel Roschka .bInterfaceSubClass = 1, 29637b848ed6SDaniel Roschka .bInterfaceProtocol = 0, 29643bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 29650c0d06caSMauro Carvalho Chehab /* Foxlink ("HP Webcam" on HP Mini 5103) */ 29660c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29670c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 29680c0d06caSMauro Carvalho Chehab .idVendor = 0x05c8, 29690c0d06caSMauro Carvalho Chehab .idProduct = 0x0403, 29700c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 29710c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 29720c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 29733bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 29740c0d06caSMauro Carvalho Chehab /* Genesys Logic USB 2.0 PC Camera */ 29750c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29760c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 29770c0d06caSMauro Carvalho Chehab .idVendor = 0x05e3, 29780c0d06caSMauro Carvalho Chehab .idProduct = 0x0505, 29790c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 29800c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 29810c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 29823bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 29830c0d06caSMauro Carvalho Chehab /* Hercules Classic Silver */ 29840c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29850c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 29860c0d06caSMauro Carvalho Chehab .idVendor = 0x06f8, 29870c0d06caSMauro Carvalho Chehab .idProduct = 0x300c, 29880c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 29890c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 29900c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 29913bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 29920c0d06caSMauro Carvalho Chehab /* ViMicro Vega */ 29930c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29940c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 29950c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 29960c0d06caSMauro Carvalho Chehab .idProduct = 0x332d, 29970c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 29980c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 29990c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 30003bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 30010c0d06caSMauro Carvalho Chehab /* ViMicro - Minoru3D */ 30020c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30030c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30040c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 30050c0d06caSMauro Carvalho Chehab .idProduct = 0x3410, 30060c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30070c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30080c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 30093bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 30100c0d06caSMauro Carvalho Chehab /* ViMicro Venus - Minoru3D */ 30110c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30120c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30130c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 30140c0d06caSMauro Carvalho Chehab .idProduct = 0x3420, 30150c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30160c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30170c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 30183bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 30190c0d06caSMauro Carvalho Chehab /* Ophir Optronics - SPCAM 620U */ 30200c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30210c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30220c0d06caSMauro Carvalho Chehab .idVendor = 0x0bd3, 30230c0d06caSMauro Carvalho Chehab .idProduct = 0x0555, 30240c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30250c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30260c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 30273bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 30280c0d06caSMauro Carvalho Chehab /* MT6227 */ 30290c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30300c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30310c0d06caSMauro Carvalho Chehab .idVendor = 0x0e8d, 30320c0d06caSMauro Carvalho Chehab .idProduct = 0x0004, 30330c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30340c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30350c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 303688d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 30373bc85817SGuennadi Liakhovetski | UVC_QUIRK_PROBE_DEF) }, 30380c0d06caSMauro Carvalho Chehab /* IMC Networks (Medion Akoya) */ 30390c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30400c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30410c0d06caSMauro Carvalho Chehab .idVendor = 0x13d3, 30420c0d06caSMauro Carvalho Chehab .idProduct = 0x5103, 30430c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30440c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30450c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 30463bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 30470c0d06caSMauro Carvalho Chehab /* JMicron USB2.0 XGA WebCam */ 30480c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30490c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30500c0d06caSMauro Carvalho Chehab .idVendor = 0x152d, 30510c0d06caSMauro Carvalho Chehab .idProduct = 0x0310, 30520c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30530c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30540c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 30553bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 30560c0d06caSMauro Carvalho Chehab /* Syntek (HP Spartan) */ 30570c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30580c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30590c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 30600c0d06caSMauro Carvalho Chehab .idProduct = 0x5212, 30610c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30620c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30630c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 30643bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 30650c0d06caSMauro Carvalho Chehab /* Syntek (Samsung Q310) */ 30660c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30670c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30680c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 30690c0d06caSMauro Carvalho Chehab .idProduct = 0x5931, 30700c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30710c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30720c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 30733bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 30740c0d06caSMauro Carvalho Chehab /* Syntek (Packard Bell EasyNote MX52 */ 30750c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30760c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30770c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 30780c0d06caSMauro Carvalho Chehab .idProduct = 0x8a12, 30790c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30800c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30810c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 30823bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 30830c0d06caSMauro Carvalho Chehab /* Syntek (Asus F9SG) */ 30840c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30850c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30860c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 30870c0d06caSMauro Carvalho Chehab .idProduct = 0x8a31, 30880c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30890c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30900c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 30913bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 30920c0d06caSMauro Carvalho Chehab /* Syntek (Asus U3S) */ 30930c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30940c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30950c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 30960c0d06caSMauro Carvalho Chehab .idProduct = 0x8a33, 30970c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30980c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30990c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 31003bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 31010c0d06caSMauro Carvalho Chehab /* Syntek (JAOtech Smart Terminal) */ 31020c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 31030c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 31040c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 31050c0d06caSMauro Carvalho Chehab .idProduct = 0x8a34, 31060c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 31070c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 31080c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 31093bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 31100c0d06caSMauro Carvalho Chehab /* Miricle 307K */ 31110c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 31120c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 31130c0d06caSMauro Carvalho Chehab .idVendor = 0x17dc, 31140c0d06caSMauro Carvalho Chehab .idProduct = 0x0202, 31150c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 31160c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 31170c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 31183bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 31190c0d06caSMauro Carvalho Chehab /* Lenovo Thinkpad SL400/SL500 */ 31200c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 31210c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 31220c0d06caSMauro Carvalho Chehab .idVendor = 0x17ef, 31230c0d06caSMauro Carvalho Chehab .idProduct = 0x480b, 31240c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 31250c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 31260c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 31273bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 31280c0d06caSMauro Carvalho Chehab /* Aveo Technology USB 2.0 Camera */ 31290c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 31300c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 31310c0d06caSMauro Carvalho Chehab .idVendor = 0x1871, 31320c0d06caSMauro Carvalho Chehab .idProduct = 0x0306, 31330c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 31340c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 31350c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 313688d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 31373bc85817SGuennadi Liakhovetski | UVC_QUIRK_PROBE_EXTRAFIELDS) }, 3138fe652471SLaurent Pinchart /* Aveo Technology USB 2.0 Camera (Tasco USB Microscope) */ 3139fe652471SLaurent Pinchart { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3140fe652471SLaurent Pinchart | USB_DEVICE_ID_MATCH_INT_INFO, 3141fe652471SLaurent Pinchart .idVendor = 0x1871, 3142fe652471SLaurent Pinchart .idProduct = 0x0516, 3143fe652471SLaurent Pinchart .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 3144fe652471SLaurent Pinchart .bInterfaceSubClass = 1, 3145fe652471SLaurent Pinchart .bInterfaceProtocol = 0 }, 31460c0d06caSMauro Carvalho Chehab /* Ecamm Pico iMage */ 31470c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 31480c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 31490c0d06caSMauro Carvalho Chehab .idVendor = 0x18cd, 31500c0d06caSMauro Carvalho Chehab .idProduct = 0xcafe, 31510c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 31520c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 31530c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 315488d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_EXTRAFIELDS) }, 31550c0d06caSMauro Carvalho Chehab /* Manta MM-353 Plako */ 31560c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 31570c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 31580c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 31590c0d06caSMauro Carvalho Chehab .idProduct = 0x3188, 31600c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 31610c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 31620c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 31633bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 31640c0d06caSMauro Carvalho Chehab /* FSC WebCam V30S */ 31650c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 31660c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 31670c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 31680c0d06caSMauro Carvalho Chehab .idProduct = 0x3288, 31690c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 31700c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 31710c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 31723bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 31730c0d06caSMauro Carvalho Chehab /* Arkmicro unbranded */ 31740c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 31750c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 31760c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 31770c0d06caSMauro Carvalho Chehab .idProduct = 0x3290, 31780c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 31790c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 31800c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 31813bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 31820c0d06caSMauro Carvalho Chehab /* The Imaging Source USB CCD cameras */ 31830c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 31840c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 31850c0d06caSMauro Carvalho Chehab .idVendor = 0x199e, 31860c0d06caSMauro Carvalho Chehab .idProduct = 0x8102, 31870c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 31880c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 31890c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 31900c0d06caSMauro Carvalho Chehab /* Bodelin ProScopeHR */ 31910c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 31920c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_DEV_HI 31930c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 31940c0d06caSMauro Carvalho Chehab .idVendor = 0x19ab, 31950c0d06caSMauro Carvalho Chehab .idProduct = 0x1000, 31960c0d06caSMauro Carvalho Chehab .bcdDevice_hi = 0x0126, 31970c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 31980c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 31990c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 320088d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_STATUS_INTERVAL) }, 32010c0d06caSMauro Carvalho Chehab /* MSI StarCam 370i */ 32020c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 32030c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 32040c0d06caSMauro Carvalho Chehab .idVendor = 0x1b3b, 32050c0d06caSMauro Carvalho Chehab .idProduct = 0x2951, 32060c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 32070c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 32080c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 32093bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 3210589266bdSNeil Armstrong /* Generalplus Technology Inc. 808 Camera */ 3211589266bdSNeil Armstrong { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3212589266bdSNeil Armstrong | USB_DEVICE_ID_MATCH_INT_INFO, 3213589266bdSNeil Armstrong .idVendor = 0x1b3f, 3214589266bdSNeil Armstrong .idProduct = 0x2002, 3215589266bdSNeil Armstrong .bInterfaceClass = USB_CLASS_VIDEO, 3216589266bdSNeil Armstrong .bInterfaceSubClass = 1, 3217589266bdSNeil Armstrong .bInterfaceProtocol = 0, 3218589266bdSNeil Armstrong .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 3219b400b6f2SLaurent Pinchart /* Shenzhen Aoni Electronic Co.,Ltd 2K FHD camera */ 3220b400b6f2SLaurent Pinchart { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3221b400b6f2SLaurent Pinchart | USB_DEVICE_ID_MATCH_INT_INFO, 3222b400b6f2SLaurent Pinchart .idVendor = 0x1bcf, 3223b400b6f2SLaurent Pinchart .idProduct = 0x0b40, 3224b400b6f2SLaurent Pinchart .bInterfaceClass = USB_CLASS_VIDEO, 3225b400b6f2SLaurent Pinchart .bInterfaceSubClass = 1, 3226b400b6f2SLaurent Pinchart .bInterfaceProtocol = 0, 3227b400b6f2SLaurent Pinchart .driver_info = (kernel_ulong_t)&(const struct uvc_device_info){ 3228b400b6f2SLaurent Pinchart .uvc_version = 0x010a, 3229b400b6f2SLaurent Pinchart } }, 32300c0d06caSMauro Carvalho Chehab /* SiGma Micro USB Web Camera */ 32310c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 32320c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 32330c0d06caSMauro Carvalho Chehab .idVendor = 0x1c4f, 32340c0d06caSMauro Carvalho Chehab .idProduct = 0x3000, 32350c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 32360c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 32370c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 323888d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 32393bc85817SGuennadi Liakhovetski | UVC_QUIRK_IGNORE_SELECTOR_UNIT) }, 3240e1b78a33SPhilipp Zabel /* Oculus VR Positional Tracker DK2 */ 3241e1b78a33SPhilipp Zabel { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3242e1b78a33SPhilipp Zabel | USB_DEVICE_ID_MATCH_INT_INFO, 3243e1b78a33SPhilipp Zabel .idVendor = 0x2833, 3244e1b78a33SPhilipp Zabel .idProduct = 0x0201, 3245e1b78a33SPhilipp Zabel .bInterfaceClass = USB_CLASS_VIDEO, 3246e1b78a33SPhilipp Zabel .bInterfaceSubClass = 1, 3247e1b78a33SPhilipp Zabel .bInterfaceProtocol = 0, 32483bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_force_y8 }, 324903c47aaeSPhilipp Zabel /* Oculus VR Rift Sensor */ 325003c47aaeSPhilipp Zabel { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 325103c47aaeSPhilipp Zabel | USB_DEVICE_ID_MATCH_INT_INFO, 325203c47aaeSPhilipp Zabel .idVendor = 0x2833, 325303c47aaeSPhilipp Zabel .idProduct = 0x0211, 325403c47aaeSPhilipp Zabel .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 325503c47aaeSPhilipp Zabel .bInterfaceSubClass = 1, 325603c47aaeSPhilipp Zabel .bInterfaceProtocol = 0, 32573bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_force_y8 }, 32581dd2e8f9SSergey Zakharchenko /* GEO Semiconductor GC6500 */ 32591dd2e8f9SSergey Zakharchenko { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 32601dd2e8f9SSergey Zakharchenko | USB_DEVICE_ID_MATCH_INT_INFO, 32611dd2e8f9SSergey Zakharchenko .idVendor = 0x29fe, 32621dd2e8f9SSergey Zakharchenko .idProduct = 0x4d53, 32631dd2e8f9SSergey Zakharchenko .bInterfaceClass = USB_CLASS_VIDEO, 32641dd2e8f9SSergey Zakharchenko .bInterfaceSubClass = 1, 32651dd2e8f9SSergey Zakharchenko .bInterfaceProtocol = 0, 32661dd2e8f9SSergey Zakharchenko .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_FORCE_BPP) }, 3267*101418b3Shuanglei /* Sonix Technology USB 2.0 Camera */ 3268*101418b3Shuanglei { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3269*101418b3Shuanglei | USB_DEVICE_ID_MATCH_INT_INFO, 3270*101418b3Shuanglei .idVendor = 0x3277, 3271*101418b3Shuanglei .idProduct = 0x0072, 3272*101418b3Shuanglei .bInterfaceClass = USB_CLASS_VIDEO, 3273*101418b3Shuanglei .bInterfaceSubClass = 1, 3274*101418b3Shuanglei .bInterfaceProtocol = 0, 3275*101418b3Shuanglei .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 3276eff1e24cSRicardo Ribalda /* Acer EasyCamera */ 3277eff1e24cSRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3278eff1e24cSRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 3279eff1e24cSRicardo Ribalda .idVendor = 0x5986, 3280eff1e24cSRicardo Ribalda .idProduct = 0x1172, 3281eff1e24cSRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 3282eff1e24cSRicardo Ribalda .bInterfaceSubClass = 1, 3283eff1e24cSRicardo Ribalda .bInterfaceProtocol = 0, 3284eff1e24cSRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 32856ea0d588SGuennadi Liakhovetski /* Intel RealSense D4M */ 32866ea0d588SGuennadi Liakhovetski { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 32876ea0d588SGuennadi Liakhovetski | USB_DEVICE_ID_MATCH_INT_INFO, 32886ea0d588SGuennadi Liakhovetski .idVendor = 0x8086, 32896ea0d588SGuennadi Liakhovetski .idProduct = 0x0b03, 32906ea0d588SGuennadi Liakhovetski .bInterfaceClass = USB_CLASS_VIDEO, 32916ea0d588SGuennadi Liakhovetski .bInterfaceSubClass = 1, 32926ea0d588SGuennadi Liakhovetski .bInterfaceProtocol = 0, 32936ea0d588SGuennadi Liakhovetski .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 32940c0d06caSMauro Carvalho Chehab /* Generic USB Video Class */ 32958afe97beSLaurent Pinchart { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_UNDEFINED) }, 32968afe97beSLaurent Pinchart { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_15) }, 32970c0d06caSMauro Carvalho Chehab {} 32980c0d06caSMauro Carvalho Chehab }; 32990c0d06caSMauro Carvalho Chehab 33000c0d06caSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, uvc_ids); 33010c0d06caSMauro Carvalho Chehab 33020c0d06caSMauro Carvalho Chehab struct uvc_driver uvc_driver = { 33030c0d06caSMauro Carvalho Chehab .driver = { 33040c0d06caSMauro Carvalho Chehab .name = "uvcvideo", 33050c0d06caSMauro Carvalho Chehab .probe = uvc_probe, 33060c0d06caSMauro Carvalho Chehab .disconnect = uvc_disconnect, 33070c0d06caSMauro Carvalho Chehab .suspend = uvc_suspend, 33080c0d06caSMauro Carvalho Chehab .resume = uvc_resume, 33090c0d06caSMauro Carvalho Chehab .reset_resume = uvc_reset_resume, 33100c0d06caSMauro Carvalho Chehab .id_table = uvc_ids, 33110c0d06caSMauro Carvalho Chehab .supports_autosuspend = 1, 33120c0d06caSMauro Carvalho Chehab }, 33130c0d06caSMauro Carvalho Chehab }; 33140c0d06caSMauro Carvalho Chehab 33150c0d06caSMauro Carvalho Chehab static int __init uvc_init(void) 33160c0d06caSMauro Carvalho Chehab { 33170c0d06caSMauro Carvalho Chehab int ret; 33180c0d06caSMauro Carvalho Chehab 33190c0d06caSMauro Carvalho Chehab uvc_debugfs_init(); 33200c0d06caSMauro Carvalho Chehab 33210c0d06caSMauro Carvalho Chehab ret = usb_register(&uvc_driver.driver); 33220c0d06caSMauro Carvalho Chehab if (ret < 0) { 33230c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup(); 33240c0d06caSMauro Carvalho Chehab return ret; 33250c0d06caSMauro Carvalho Chehab } 33260c0d06caSMauro Carvalho Chehab 33270c0d06caSMauro Carvalho Chehab return 0; 33280c0d06caSMauro Carvalho Chehab } 33290c0d06caSMauro Carvalho Chehab 33300c0d06caSMauro Carvalho Chehab static void __exit uvc_cleanup(void) 33310c0d06caSMauro Carvalho Chehab { 33320c0d06caSMauro Carvalho Chehab usb_deregister(&uvc_driver.driver); 33330c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup(); 33340c0d06caSMauro Carvalho Chehab } 33350c0d06caSMauro Carvalho Chehab 33360c0d06caSMauro Carvalho Chehab module_init(uvc_init); 33370c0d06caSMauro Carvalho Chehab module_exit(uvc_cleanup); 33380c0d06caSMauro Carvalho Chehab 33390c0d06caSMauro Carvalho Chehab MODULE_AUTHOR(DRIVER_AUTHOR); 33400c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION(DRIVER_DESC); 33410c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 33420c0d06caSMauro Carvalho Chehab MODULE_VERSION(DRIVER_VERSION); 33430c0d06caSMauro Carvalho Chehab 3344