10c0d06caSMauro Carvalho Chehab /* 20c0d06caSMauro Carvalho Chehab * uvc_driver.c -- USB Video Class driver 30c0d06caSMauro Carvalho Chehab * 40c0d06caSMauro Carvalho Chehab * Copyright (C) 2005-2010 50c0d06caSMauro Carvalho Chehab * Laurent Pinchart (laurent.pinchart@ideasonboard.com) 60c0d06caSMauro Carvalho Chehab * 70c0d06caSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 80c0d06caSMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 90c0d06caSMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 100c0d06caSMauro Carvalho Chehab * (at your option) any later version. 110c0d06caSMauro Carvalho Chehab * 120c0d06caSMauro Carvalho Chehab */ 130c0d06caSMauro Carvalho Chehab 140c0d06caSMauro Carvalho Chehab #include <linux/atomic.h> 150c0d06caSMauro Carvalho Chehab #include <linux/kernel.h> 160c0d06caSMauro Carvalho Chehab #include <linux/list.h> 170c0d06caSMauro Carvalho Chehab #include <linux/module.h> 180c0d06caSMauro Carvalho Chehab #include <linux/slab.h> 190c0d06caSMauro Carvalho Chehab #include <linux/usb.h> 200c0d06caSMauro Carvalho Chehab #include <linux/videodev2.h> 210c0d06caSMauro Carvalho Chehab #include <linux/vmalloc.h> 220c0d06caSMauro Carvalho Chehab #include <linux/wait.h> 230c0d06caSMauro Carvalho Chehab #include <linux/version.h> 240c0d06caSMauro Carvalho Chehab #include <asm/unaligned.h> 250c0d06caSMauro Carvalho Chehab 260c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h> 2731a96f4cSLaurent Pinchart #include <media/v4l2-ioctl.h> 280c0d06caSMauro Carvalho Chehab 290c0d06caSMauro Carvalho Chehab #include "uvcvideo.h" 300c0d06caSMauro Carvalho Chehab 310c0d06caSMauro Carvalho Chehab #define DRIVER_AUTHOR "Laurent Pinchart " \ 320c0d06caSMauro Carvalho Chehab "<laurent.pinchart@ideasonboard.com>" 330c0d06caSMauro Carvalho Chehab #define DRIVER_DESC "USB Video Class driver" 340c0d06caSMauro Carvalho Chehab 350c0d06caSMauro Carvalho Chehab unsigned int uvc_clock_param = CLOCK_MONOTONIC; 365d0fd3c8SLaurent Pinchart unsigned int uvc_hw_timestamps_param; 370c0d06caSMauro Carvalho Chehab unsigned int uvc_no_drop_param; 380c0d06caSMauro Carvalho Chehab static unsigned int uvc_quirks_param = -1; 390c0d06caSMauro Carvalho Chehab unsigned int uvc_trace_param; 400c0d06caSMauro Carvalho Chehab unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; 410c0d06caSMauro Carvalho Chehab 420c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 430c0d06caSMauro Carvalho Chehab * Video formats 440c0d06caSMauro Carvalho Chehab */ 450c0d06caSMauro Carvalho Chehab 460c0d06caSMauro Carvalho Chehab static struct uvc_format_desc uvc_fmts[] = { 470c0d06caSMauro Carvalho Chehab { 480c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:2 (YUYV)", 490c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_YUY2, 500c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_YUYV, 510c0d06caSMauro Carvalho Chehab }, 520c0d06caSMauro Carvalho Chehab { 530c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:2 (YUYV)", 540c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_YUY2_ISIGHT, 550c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_YUYV, 560c0d06caSMauro Carvalho Chehab }, 570c0d06caSMauro Carvalho Chehab { 580c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:0 (NV12)", 590c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_NV12, 600c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_NV12, 610c0d06caSMauro Carvalho Chehab }, 620c0d06caSMauro Carvalho Chehab { 630c0d06caSMauro Carvalho Chehab .name = "MJPEG", 640c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_MJPEG, 650c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_MJPEG, 660c0d06caSMauro Carvalho Chehab }, 670c0d06caSMauro Carvalho Chehab { 680c0d06caSMauro Carvalho Chehab .name = "YVU 4:2:0 (YV12)", 690c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_YV12, 700c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_YVU420, 710c0d06caSMauro Carvalho Chehab }, 720c0d06caSMauro Carvalho Chehab { 730c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:0 (I420)", 740c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_I420, 750c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_YUV420, 760c0d06caSMauro Carvalho Chehab }, 770c0d06caSMauro Carvalho Chehab { 780c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:0 (M420)", 790c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_M420, 800c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_M420, 810c0d06caSMauro Carvalho Chehab }, 820c0d06caSMauro Carvalho Chehab { 830c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:2 (UYVY)", 840c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_UYVY, 850c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_UYVY, 860c0d06caSMauro Carvalho Chehab }, 870c0d06caSMauro Carvalho Chehab { 880c0d06caSMauro Carvalho Chehab .name = "Greyscale 8-bit (Y800)", 890c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y800, 900c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_GREY, 910c0d06caSMauro Carvalho Chehab }, 920c0d06caSMauro Carvalho Chehab { 930c0d06caSMauro Carvalho Chehab .name = "Greyscale 8-bit (Y8 )", 940c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y8, 950c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_GREY, 960c0d06caSMauro Carvalho Chehab }, 970c0d06caSMauro Carvalho Chehab { 98e96cdc9aSNicolas Dufresne .name = "Greyscale 8-bit (D3DFMT_L8)", 99e96cdc9aSNicolas Dufresne .guid = UVC_GUID_FORMAT_D3DFMT_L8, 100e96cdc9aSNicolas Dufresne .fcc = V4L2_PIX_FMT_GREY, 101e96cdc9aSNicolas Dufresne }, 102e96cdc9aSNicolas Dufresne { 103557a5c7fSLaurent Pinchart .name = "IR 8-bit (L8_IR)", 104557a5c7fSLaurent Pinchart .guid = UVC_GUID_FORMAT_KSMEDIA_L8_IR, 105557a5c7fSLaurent Pinchart .fcc = V4L2_PIX_FMT_GREY, 106557a5c7fSLaurent Pinchart }, 107557a5c7fSLaurent Pinchart { 1080c0d06caSMauro Carvalho Chehab .name = "Greyscale 10-bit (Y10 )", 1090c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y10, 1100c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_Y10, 1110c0d06caSMauro Carvalho Chehab }, 1120c0d06caSMauro Carvalho Chehab { 1130c0d06caSMauro Carvalho Chehab .name = "Greyscale 12-bit (Y12 )", 1140c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y12, 1150c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_Y12, 1160c0d06caSMauro Carvalho Chehab }, 1170c0d06caSMauro Carvalho Chehab { 1180c0d06caSMauro Carvalho Chehab .name = "Greyscale 16-bit (Y16 )", 1190c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y16, 1200c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_Y16, 1210c0d06caSMauro Carvalho Chehab }, 1220c0d06caSMauro Carvalho Chehab { 123e72ed08eSEdgar Thier .name = "BGGR Bayer (BY8 )", 1240c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_BY8, 1250c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_SBGGR8, 1260c0d06caSMauro Carvalho Chehab }, 1270c0d06caSMauro Carvalho Chehab { 128e72ed08eSEdgar Thier .name = "BGGR Bayer (BA81)", 129e72ed08eSEdgar Thier .guid = UVC_GUID_FORMAT_BA81, 130e72ed08eSEdgar Thier .fcc = V4L2_PIX_FMT_SBGGR8, 131e72ed08eSEdgar Thier }, 132e72ed08eSEdgar Thier { 133e72ed08eSEdgar Thier .name = "GBRG Bayer (GBRG)", 134e72ed08eSEdgar Thier .guid = UVC_GUID_FORMAT_GBRG, 135e72ed08eSEdgar Thier .fcc = V4L2_PIX_FMT_SGBRG8, 136e72ed08eSEdgar Thier }, 137e72ed08eSEdgar Thier { 138e72ed08eSEdgar Thier .name = "GRBG Bayer (GRBG)", 139e72ed08eSEdgar Thier .guid = UVC_GUID_FORMAT_GRBG, 140e72ed08eSEdgar Thier .fcc = V4L2_PIX_FMT_SGRBG8, 141e72ed08eSEdgar Thier }, 142e72ed08eSEdgar Thier { 143e72ed08eSEdgar Thier .name = "RGGB Bayer (RGGB)", 144e72ed08eSEdgar Thier .guid = UVC_GUID_FORMAT_RGGB, 145e72ed08eSEdgar Thier .fcc = V4L2_PIX_FMT_SRGGB8, 146e72ed08eSEdgar Thier }, 147e72ed08eSEdgar Thier { 1480c0d06caSMauro Carvalho Chehab .name = "RGB565", 1490c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_RGBP, 1500c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_RGB565, 1510c0d06caSMauro Carvalho Chehab }, 1520c0d06caSMauro Carvalho Chehab { 1531b8dc322SWilliam Manley .name = "BGR 8:8:8 (BGR3)", 1541b8dc322SWilliam Manley .guid = UVC_GUID_FORMAT_BGR3, 1551b8dc322SWilliam Manley .fcc = V4L2_PIX_FMT_BGR24, 1561b8dc322SWilliam Manley }, 1571b8dc322SWilliam Manley { 1580c0d06caSMauro Carvalho Chehab .name = "H.264", 1590c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_H264, 1600c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_H264, 1610c0d06caSMauro Carvalho Chehab }, 1625d8d8db8SAviv Greenberg { 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 }, 2220c0d06caSMauro Carvalho Chehab }; 2230c0d06caSMauro Carvalho Chehab 2240c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 2250c0d06caSMauro Carvalho Chehab * Utility functions 2260c0d06caSMauro Carvalho Chehab */ 2270c0d06caSMauro Carvalho Chehab 2280c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, 2292c6b222cSLaurent Pinchart u8 epaddr) 2300c0d06caSMauro Carvalho Chehab { 2310c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep; 2320c0d06caSMauro Carvalho Chehab unsigned int i; 2330c0d06caSMauro Carvalho Chehab 2340c0d06caSMauro Carvalho Chehab for (i = 0; i < alts->desc.bNumEndpoints; ++i) { 2350c0d06caSMauro Carvalho Chehab ep = &alts->endpoint[i]; 2360c0d06caSMauro Carvalho Chehab if (ep->desc.bEndpointAddress == epaddr) 2370c0d06caSMauro Carvalho Chehab return ep; 2380c0d06caSMauro Carvalho Chehab } 2390c0d06caSMauro Carvalho Chehab 2400c0d06caSMauro Carvalho Chehab return NULL; 2410c0d06caSMauro Carvalho Chehab } 2420c0d06caSMauro Carvalho Chehab 2432c6b222cSLaurent Pinchart static struct uvc_format_desc *uvc_format_by_guid(const u8 guid[16]) 2440c0d06caSMauro Carvalho Chehab { 2450c0d06caSMauro Carvalho Chehab unsigned int len = ARRAY_SIZE(uvc_fmts); 2460c0d06caSMauro Carvalho Chehab unsigned int i; 2470c0d06caSMauro Carvalho Chehab 2480c0d06caSMauro Carvalho Chehab for (i = 0; i < len; ++i) { 2490c0d06caSMauro Carvalho Chehab if (memcmp(guid, uvc_fmts[i].guid, 16) == 0) 2500c0d06caSMauro Carvalho Chehab return &uvc_fmts[i]; 2510c0d06caSMauro Carvalho Chehab } 2520c0d06caSMauro Carvalho Chehab 2530c0d06caSMauro Carvalho Chehab return NULL; 2540c0d06caSMauro Carvalho Chehab } 2550c0d06caSMauro Carvalho Chehab 2562c6b222cSLaurent Pinchart static u32 uvc_colorspace(const u8 primaries) 2570c0d06caSMauro Carvalho Chehab { 2582c6b222cSLaurent Pinchart static const u8 colorprimaries[] = { 2590c0d06caSMauro Carvalho Chehab 0, 2600c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SRGB, 2610c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_470_SYSTEM_M, 2620c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_470_SYSTEM_BG, 2630c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SMPTE170M, 2640c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SMPTE240M, 2650c0d06caSMauro Carvalho Chehab }; 2660c0d06caSMauro Carvalho Chehab 2670c0d06caSMauro Carvalho Chehab if (primaries < ARRAY_SIZE(colorprimaries)) 2680c0d06caSMauro Carvalho Chehab return colorprimaries[primaries]; 2690c0d06caSMauro Carvalho Chehab 2700c0d06caSMauro Carvalho Chehab return 0; 2710c0d06caSMauro Carvalho Chehab } 2720c0d06caSMauro Carvalho Chehab 2730c0d06caSMauro Carvalho Chehab /* Simplify a fraction using a simple continued fraction decomposition. The 2740c0d06caSMauro Carvalho Chehab * idea here is to convert fractions such as 333333/10000000 to 1/30 using 2750c0d06caSMauro Carvalho Chehab * 32 bit arithmetic only. The algorithm is not perfect and relies upon two 2760c0d06caSMauro Carvalho Chehab * arbitrary parameters to remove non-significative terms from the simple 2770c0d06caSMauro Carvalho Chehab * continued fraction decomposition. Using 8 and 333 for n_terms and threshold 2780c0d06caSMauro Carvalho Chehab * respectively seems to give nice results. 2790c0d06caSMauro Carvalho Chehab */ 2801e304c47SLaurent Pinchart void uvc_simplify_fraction(u32 *numerator, u32 *denominator, 2810c0d06caSMauro Carvalho Chehab unsigned int n_terms, unsigned int threshold) 2820c0d06caSMauro Carvalho Chehab { 2831e304c47SLaurent Pinchart u32 *an; 2841e304c47SLaurent Pinchart u32 x, y, r; 2850c0d06caSMauro Carvalho Chehab unsigned int i, n; 2860c0d06caSMauro Carvalho Chehab 287f14d4988SLaurent Pinchart an = kmalloc_array(n_terms, sizeof(*an), GFP_KERNEL); 2880c0d06caSMauro Carvalho Chehab if (an == NULL) 2890c0d06caSMauro Carvalho Chehab return; 2900c0d06caSMauro Carvalho Chehab 2910c0d06caSMauro Carvalho Chehab /* Convert the fraction to a simple continued fraction. See 2920c0d06caSMauro Carvalho Chehab * http://mathforum.org/dr.math/faq/faq.fractions.html 2930c0d06caSMauro Carvalho Chehab * Stop if the current term is bigger than or equal to the given 2940c0d06caSMauro Carvalho Chehab * threshold. 2950c0d06caSMauro Carvalho Chehab */ 2960c0d06caSMauro Carvalho Chehab x = *numerator; 2970c0d06caSMauro Carvalho Chehab y = *denominator; 2980c0d06caSMauro Carvalho Chehab 2990c0d06caSMauro Carvalho Chehab for (n = 0; n < n_terms && y != 0; ++n) { 3000c0d06caSMauro Carvalho Chehab an[n] = x / y; 3010c0d06caSMauro Carvalho Chehab if (an[n] >= threshold) { 3020c0d06caSMauro Carvalho Chehab if (n < 2) 3030c0d06caSMauro Carvalho Chehab n++; 3040c0d06caSMauro Carvalho Chehab break; 3050c0d06caSMauro Carvalho Chehab } 3060c0d06caSMauro Carvalho Chehab 3070c0d06caSMauro Carvalho Chehab r = x - an[n] * y; 3080c0d06caSMauro Carvalho Chehab x = y; 3090c0d06caSMauro Carvalho Chehab y = r; 3100c0d06caSMauro Carvalho Chehab } 3110c0d06caSMauro Carvalho Chehab 3120c0d06caSMauro Carvalho Chehab /* Expand the simple continued fraction back to an integer fraction. */ 3130c0d06caSMauro Carvalho Chehab x = 0; 3140c0d06caSMauro Carvalho Chehab y = 1; 3150c0d06caSMauro Carvalho Chehab 3160c0d06caSMauro Carvalho Chehab for (i = n; i > 0; --i) { 3170c0d06caSMauro Carvalho Chehab r = y; 3180c0d06caSMauro Carvalho Chehab y = an[i-1] * y + x; 3190c0d06caSMauro Carvalho Chehab x = r; 3200c0d06caSMauro Carvalho Chehab } 3210c0d06caSMauro Carvalho Chehab 3220c0d06caSMauro Carvalho Chehab *numerator = y; 3230c0d06caSMauro Carvalho Chehab *denominator = x; 3240c0d06caSMauro Carvalho Chehab kfree(an); 3250c0d06caSMauro Carvalho Chehab } 3260c0d06caSMauro Carvalho Chehab 3270c0d06caSMauro Carvalho Chehab /* Convert a fraction to a frame interval in 100ns multiples. The idea here is 3280c0d06caSMauro Carvalho Chehab * to compute numerator / denominator * 10000000 using 32 bit fixed point 3290c0d06caSMauro Carvalho Chehab * arithmetic only. 3300c0d06caSMauro Carvalho Chehab */ 3311e304c47SLaurent Pinchart u32 uvc_fraction_to_interval(u32 numerator, u32 denominator) 3320c0d06caSMauro Carvalho Chehab { 3331e304c47SLaurent Pinchart u32 multiplier; 3340c0d06caSMauro Carvalho Chehab 3350c0d06caSMauro Carvalho Chehab /* Saturate the result if the operation would overflow. */ 3360c0d06caSMauro Carvalho Chehab if (denominator == 0 || 3371e304c47SLaurent Pinchart numerator/denominator >= ((u32)-1)/10000000) 3381e304c47SLaurent Pinchart return (u32)-1; 3390c0d06caSMauro Carvalho Chehab 3400c0d06caSMauro Carvalho Chehab /* Divide both the denominator and the multiplier by two until 3410c0d06caSMauro Carvalho Chehab * numerator * multiplier doesn't overflow. If anyone knows a better 3420c0d06caSMauro Carvalho Chehab * algorithm please let me know. 3430c0d06caSMauro Carvalho Chehab */ 3440c0d06caSMauro Carvalho Chehab multiplier = 10000000; 3451e304c47SLaurent Pinchart while (numerator > ((u32)-1)/multiplier) { 3460c0d06caSMauro Carvalho Chehab multiplier /= 2; 3470c0d06caSMauro Carvalho Chehab denominator /= 2; 3480c0d06caSMauro Carvalho Chehab } 3490c0d06caSMauro Carvalho Chehab 3500c0d06caSMauro Carvalho Chehab return denominator ? numerator * multiplier / denominator : 0; 3510c0d06caSMauro Carvalho Chehab } 3520c0d06caSMauro Carvalho Chehab 3530c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 3540c0d06caSMauro Carvalho Chehab * Terminal and unit management 3550c0d06caSMauro Carvalho Chehab */ 3560c0d06caSMauro Carvalho Chehab 3570c0d06caSMauro Carvalho Chehab struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id) 3580c0d06caSMauro Carvalho Chehab { 3590c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 3600c0d06caSMauro Carvalho Chehab 3610c0d06caSMauro Carvalho Chehab list_for_each_entry(entity, &dev->entities, list) { 3620c0d06caSMauro Carvalho Chehab if (entity->id == id) 3630c0d06caSMauro Carvalho Chehab return entity; 3640c0d06caSMauro Carvalho Chehab } 3650c0d06caSMauro Carvalho Chehab 3660c0d06caSMauro Carvalho Chehab return NULL; 3670c0d06caSMauro Carvalho Chehab } 3680c0d06caSMauro Carvalho Chehab 3690c0d06caSMauro Carvalho Chehab static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, 3700c0d06caSMauro Carvalho Chehab int id, struct uvc_entity *entity) 3710c0d06caSMauro Carvalho Chehab { 3720c0d06caSMauro Carvalho Chehab unsigned int i; 3730c0d06caSMauro Carvalho Chehab 3740c0d06caSMauro Carvalho Chehab if (entity == NULL) 3750c0d06caSMauro Carvalho Chehab entity = list_entry(&dev->entities, struct uvc_entity, list); 3760c0d06caSMauro Carvalho Chehab 3770c0d06caSMauro Carvalho Chehab list_for_each_entry_continue(entity, &dev->entities, list) { 3780c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->bNrInPins; ++i) 3790c0d06caSMauro Carvalho Chehab if (entity->baSourceID[i] == id) 3800c0d06caSMauro Carvalho Chehab return entity; 3810c0d06caSMauro Carvalho Chehab } 3820c0d06caSMauro Carvalho Chehab 3830c0d06caSMauro Carvalho Chehab return NULL; 3840c0d06caSMauro Carvalho Chehab } 3850c0d06caSMauro Carvalho Chehab 3860c0d06caSMauro Carvalho Chehab static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id) 3870c0d06caSMauro Carvalho Chehab { 3880c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 3890c0d06caSMauro Carvalho Chehab 3900c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 3910c0d06caSMauro Carvalho Chehab if (stream->header.bTerminalLink == id) 3920c0d06caSMauro Carvalho Chehab return stream; 3930c0d06caSMauro Carvalho Chehab } 3940c0d06caSMauro Carvalho Chehab 3950c0d06caSMauro Carvalho Chehab return NULL; 3960c0d06caSMauro Carvalho Chehab } 3970c0d06caSMauro Carvalho Chehab 3980c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 399ece41454SKieran Bingham * Streaming Object Management 400ece41454SKieran Bingham */ 401ece41454SKieran Bingham 402ece41454SKieran Bingham static void uvc_stream_delete(struct uvc_streaming *stream) 403ece41454SKieran Bingham { 404*b012186aSKieran Bingham if (stream->async_wq) 405*b012186aSKieran Bingham destroy_workqueue(stream->async_wq); 406*b012186aSKieran Bingham 407ece41454SKieran Bingham mutex_destroy(&stream->mutex); 408ece41454SKieran Bingham 409ece41454SKieran Bingham usb_put_intf(stream->intf); 410ece41454SKieran Bingham 411ece41454SKieran Bingham kfree(stream->format); 412ece41454SKieran Bingham kfree(stream->header.bmaControls); 413ece41454SKieran Bingham kfree(stream); 414ece41454SKieran Bingham } 415ece41454SKieran Bingham 416ece41454SKieran Bingham static struct uvc_streaming *uvc_stream_new(struct uvc_device *dev, 417ece41454SKieran Bingham struct usb_interface *intf) 418ece41454SKieran Bingham { 419ece41454SKieran Bingham struct uvc_streaming *stream; 420ece41454SKieran Bingham 421ece41454SKieran Bingham stream = kzalloc(sizeof(*stream), GFP_KERNEL); 422ece41454SKieran Bingham if (stream == NULL) 423ece41454SKieran Bingham return NULL; 424ece41454SKieran Bingham 425ece41454SKieran Bingham mutex_init(&stream->mutex); 426ece41454SKieran Bingham 427ece41454SKieran Bingham stream->dev = dev; 428ece41454SKieran Bingham stream->intf = usb_get_intf(intf); 429ece41454SKieran Bingham stream->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; 430ece41454SKieran Bingham 431*b012186aSKieran Bingham /* Allocate a stream specific work queue for asynchronous tasks. */ 432*b012186aSKieran Bingham stream->async_wq = alloc_workqueue("uvcvideo", WQ_UNBOUND | WQ_HIGHPRI, 433*b012186aSKieran Bingham 0); 434*b012186aSKieran Bingham if (!stream->async_wq) { 435*b012186aSKieran Bingham uvc_stream_delete(stream); 436*b012186aSKieran Bingham return NULL; 437*b012186aSKieran Bingham } 438*b012186aSKieran Bingham 439ece41454SKieran Bingham return stream; 440ece41454SKieran Bingham } 441ece41454SKieran Bingham 442ece41454SKieran Bingham /* ------------------------------------------------------------------------ 4430c0d06caSMauro Carvalho Chehab * Descriptors parsing 4440c0d06caSMauro Carvalho Chehab */ 4450c0d06caSMauro Carvalho Chehab 4460c0d06caSMauro Carvalho Chehab static int uvc_parse_format(struct uvc_device *dev, 4470c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming, struct uvc_format *format, 4482c6b222cSLaurent Pinchart u32 **intervals, unsigned char *buffer, int buflen) 4490c0d06caSMauro Carvalho Chehab { 4500c0d06caSMauro Carvalho Chehab struct usb_interface *intf = streaming->intf; 4510c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = intf->cur_altsetting; 4520c0d06caSMauro Carvalho Chehab struct uvc_format_desc *fmtdesc; 4530c0d06caSMauro Carvalho Chehab struct uvc_frame *frame; 4540c0d06caSMauro Carvalho Chehab const unsigned char *start = buffer; 455e1b78a33SPhilipp Zabel unsigned int width_multiplier = 1; 4560c0d06caSMauro Carvalho Chehab unsigned int interval; 4570c0d06caSMauro Carvalho Chehab unsigned int i, n; 4582c6b222cSLaurent Pinchart u8 ftype; 4590c0d06caSMauro Carvalho Chehab 4600c0d06caSMauro Carvalho Chehab format->type = buffer[2]; 4610c0d06caSMauro Carvalho Chehab format->index = buffer[3]; 4620c0d06caSMauro Carvalho Chehab 4630c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 4640c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 4650c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 4660c0d06caSMauro Carvalho Chehab n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28; 4670c0d06caSMauro Carvalho Chehab if (buflen < n) { 4680c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 4690c0d06caSMauro Carvalho Chehab "interface %d FORMAT error\n", 4700c0d06caSMauro Carvalho Chehab dev->udev->devnum, 4710c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 4720c0d06caSMauro Carvalho Chehab return -EINVAL; 4730c0d06caSMauro Carvalho Chehab } 4740c0d06caSMauro Carvalho Chehab 4750c0d06caSMauro Carvalho Chehab /* Find the format descriptor from its GUID. */ 4760c0d06caSMauro Carvalho Chehab fmtdesc = uvc_format_by_guid(&buffer[5]); 4770c0d06caSMauro Carvalho Chehab 4780c0d06caSMauro Carvalho Chehab if (fmtdesc != NULL) { 479c0decac1SMauro Carvalho Chehab strscpy(format->name, fmtdesc->name, 480f14d4988SLaurent Pinchart sizeof(format->name)); 4810c0d06caSMauro Carvalho Chehab format->fcc = fmtdesc->fcc; 4820c0d06caSMauro Carvalho Chehab } else { 4830c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "Unknown video format %pUl\n", 4840c0d06caSMauro Carvalho Chehab &buffer[5]); 4850c0d06caSMauro Carvalho Chehab snprintf(format->name, sizeof(format->name), "%pUl\n", 4860c0d06caSMauro Carvalho Chehab &buffer[5]); 4870c0d06caSMauro Carvalho Chehab format->fcc = 0; 4880c0d06caSMauro Carvalho Chehab } 4890c0d06caSMauro Carvalho Chehab 4900c0d06caSMauro Carvalho Chehab format->bpp = buffer[21]; 491e1b78a33SPhilipp Zabel 492e1b78a33SPhilipp Zabel /* Some devices report a format that doesn't match what they 493e1b78a33SPhilipp Zabel * really send. 494e1b78a33SPhilipp Zabel */ 495e1b78a33SPhilipp Zabel if (dev->quirks & UVC_QUIRK_FORCE_Y8) { 496e1b78a33SPhilipp Zabel if (format->fcc == V4L2_PIX_FMT_YUYV) { 497c0decac1SMauro Carvalho Chehab strscpy(format->name, "Greyscale 8-bit (Y8 )", 498e1b78a33SPhilipp Zabel sizeof(format->name)); 499e1b78a33SPhilipp Zabel format->fcc = V4L2_PIX_FMT_GREY; 500e1b78a33SPhilipp Zabel format->bpp = 8; 501e1b78a33SPhilipp Zabel width_multiplier = 2; 502e1b78a33SPhilipp Zabel } 503e1b78a33SPhilipp Zabel } 504e1b78a33SPhilipp Zabel 5050c0d06caSMauro Carvalho Chehab if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) { 5060c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_UNCOMPRESSED; 5070c0d06caSMauro Carvalho Chehab } else { 5080c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_FRAME_BASED; 5090c0d06caSMauro Carvalho Chehab if (buffer[27]) 5100c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED; 5110c0d06caSMauro Carvalho Chehab } 5120c0d06caSMauro Carvalho Chehab break; 5130c0d06caSMauro Carvalho Chehab 5140c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 5150c0d06caSMauro Carvalho Chehab if (buflen < 11) { 5160c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 5170c0d06caSMauro Carvalho Chehab "interface %d FORMAT error\n", 5180c0d06caSMauro Carvalho Chehab dev->udev->devnum, 5190c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 5200c0d06caSMauro Carvalho Chehab return -EINVAL; 5210c0d06caSMauro Carvalho Chehab } 5220c0d06caSMauro Carvalho Chehab 523c0decac1SMauro Carvalho Chehab strscpy(format->name, "MJPEG", sizeof(format->name)); 5240c0d06caSMauro Carvalho Chehab format->fcc = V4L2_PIX_FMT_MJPEG; 5250c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED; 5260c0d06caSMauro Carvalho Chehab format->bpp = 0; 5270c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_MJPEG; 5280c0d06caSMauro Carvalho Chehab break; 5290c0d06caSMauro Carvalho Chehab 5300c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 5310c0d06caSMauro Carvalho Chehab if (buflen < 9) { 5320c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 5330c0d06caSMauro Carvalho Chehab "interface %d FORMAT error\n", 5340c0d06caSMauro Carvalho Chehab dev->udev->devnum, 5350c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 5360c0d06caSMauro Carvalho Chehab return -EINVAL; 5370c0d06caSMauro Carvalho Chehab } 5380c0d06caSMauro Carvalho Chehab 5390c0d06caSMauro Carvalho Chehab switch (buffer[8] & 0x7f) { 5400c0d06caSMauro Carvalho Chehab case 0: 541c0decac1SMauro Carvalho Chehab strscpy(format->name, "SD-DV", sizeof(format->name)); 5420c0d06caSMauro Carvalho Chehab break; 5430c0d06caSMauro Carvalho Chehab case 1: 544c0decac1SMauro Carvalho Chehab strscpy(format->name, "SDL-DV", sizeof(format->name)); 5450c0d06caSMauro Carvalho Chehab break; 5460c0d06caSMauro Carvalho Chehab case 2: 547c0decac1SMauro Carvalho Chehab strscpy(format->name, "HD-DV", sizeof(format->name)); 5480c0d06caSMauro Carvalho Chehab break; 5490c0d06caSMauro Carvalho Chehab default: 5500c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 5510c0d06caSMauro Carvalho Chehab "interface %d: unknown DV format %u\n", 5520c0d06caSMauro Carvalho Chehab dev->udev->devnum, 5530c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, buffer[8]); 5540c0d06caSMauro Carvalho Chehab return -EINVAL; 5550c0d06caSMauro Carvalho Chehab } 5560c0d06caSMauro Carvalho Chehab 5570c0d06caSMauro Carvalho Chehab strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz", 558f14d4988SLaurent Pinchart sizeof(format->name)); 5590c0d06caSMauro Carvalho Chehab 5600c0d06caSMauro Carvalho Chehab format->fcc = V4L2_PIX_FMT_DV; 5610c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM; 5620c0d06caSMauro Carvalho Chehab format->bpp = 0; 5630c0d06caSMauro Carvalho Chehab ftype = 0; 5640c0d06caSMauro Carvalho Chehab 5650c0d06caSMauro Carvalho Chehab /* Create a dummy frame descriptor. */ 5660c0d06caSMauro Carvalho Chehab frame = &format->frame[0]; 567f14d4988SLaurent Pinchart memset(&format->frame[0], 0, sizeof(format->frame[0])); 5680c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = 1; 5690c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 1; 5700c0d06caSMauro Carvalho Chehab frame->dwFrameInterval = *intervals; 5710c0d06caSMauro Carvalho Chehab *(*intervals)++ = 1; 5720c0d06caSMauro Carvalho Chehab format->nframes = 1; 5730c0d06caSMauro Carvalho Chehab break; 5740c0d06caSMauro Carvalho Chehab 5750c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MPEG2TS: 5760c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_STREAM_BASED: 5770c0d06caSMauro Carvalho Chehab /* Not supported yet. */ 5780c0d06caSMauro Carvalho Chehab default: 5790c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 5800c0d06caSMauro Carvalho Chehab "interface %d unsupported format %u\n", 5810c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber, 5820c0d06caSMauro Carvalho Chehab buffer[2]); 5830c0d06caSMauro Carvalho Chehab return -EINVAL; 5840c0d06caSMauro Carvalho Chehab } 5850c0d06caSMauro Carvalho Chehab 5860c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name); 5870c0d06caSMauro Carvalho Chehab 5880c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 5890c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 5900c0d06caSMauro Carvalho Chehab 5910c0d06caSMauro Carvalho Chehab /* Parse the frame descriptors. Only uncompressed, MJPEG and frame 5920c0d06caSMauro Carvalho Chehab * based formats have frame descriptors. 5930c0d06caSMauro Carvalho Chehab */ 5940c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 5950c0d06caSMauro Carvalho Chehab buffer[2] == ftype) { 5960c0d06caSMauro Carvalho Chehab frame = &format->frame[format->nframes]; 5970c0d06caSMauro Carvalho Chehab if (ftype != UVC_VS_FRAME_FRAME_BASED) 5980c0d06caSMauro Carvalho Chehab n = buflen > 25 ? buffer[25] : 0; 5990c0d06caSMauro Carvalho Chehab else 6000c0d06caSMauro Carvalho Chehab n = buflen > 21 ? buffer[21] : 0; 6010c0d06caSMauro Carvalho Chehab 6020c0d06caSMauro Carvalho Chehab n = n ? n : 3; 6030c0d06caSMauro Carvalho Chehab 6040c0d06caSMauro Carvalho Chehab if (buflen < 26 + 4*n) { 6050c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 6060c0d06caSMauro Carvalho Chehab "interface %d FRAME error\n", dev->udev->devnum, 6070c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 6080c0d06caSMauro Carvalho Chehab return -EINVAL; 6090c0d06caSMauro Carvalho Chehab } 6100c0d06caSMauro Carvalho Chehab 6110c0d06caSMauro Carvalho Chehab frame->bFrameIndex = buffer[3]; 6120c0d06caSMauro Carvalho Chehab frame->bmCapabilities = buffer[4]; 613e1b78a33SPhilipp Zabel frame->wWidth = get_unaligned_le16(&buffer[5]) 614e1b78a33SPhilipp Zabel * width_multiplier; 6150c0d06caSMauro Carvalho Chehab frame->wHeight = get_unaligned_le16(&buffer[7]); 6160c0d06caSMauro Carvalho Chehab frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); 6170c0d06caSMauro Carvalho Chehab frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); 6180c0d06caSMauro Carvalho Chehab if (ftype != UVC_VS_FRAME_FRAME_BASED) { 6190c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = 6200c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[17]); 6210c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 6220c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[21]); 6230c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = buffer[25]; 6240c0d06caSMauro Carvalho Chehab } else { 6250c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = 0; 6260c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 6270c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[17]); 6280c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = buffer[21]; 6290c0d06caSMauro Carvalho Chehab } 6300c0d06caSMauro Carvalho Chehab frame->dwFrameInterval = *intervals; 6310c0d06caSMauro Carvalho Chehab 6320c0d06caSMauro Carvalho Chehab /* Several UVC chipsets screw up dwMaxVideoFrameBufferSize 6330c0d06caSMauro Carvalho Chehab * completely. Observed behaviours range from setting the 6340c0d06caSMauro Carvalho Chehab * value to 1.1x the actual frame size to hardwiring the 6350c0d06caSMauro Carvalho Chehab * 16 low bits to 0. This results in a higher than necessary 6360c0d06caSMauro Carvalho Chehab * memory usage as well as a wrong image size information. For 6370c0d06caSMauro Carvalho Chehab * uncompressed formats this can be fixed by computing the 6380c0d06caSMauro Carvalho Chehab * value from the frame size. 6390c0d06caSMauro Carvalho Chehab */ 6400c0d06caSMauro Carvalho Chehab if (!(format->flags & UVC_FMT_FLAG_COMPRESSED)) 6410c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = format->bpp 6420c0d06caSMauro Carvalho Chehab * frame->wWidth * frame->wHeight / 8; 6430c0d06caSMauro Carvalho Chehab 6440c0d06caSMauro Carvalho Chehab /* Some bogus devices report dwMinFrameInterval equal to 6450c0d06caSMauro Carvalho Chehab * dwMaxFrameInterval and have dwFrameIntervalStep set to 6460c0d06caSMauro Carvalho Chehab * zero. Setting all null intervals to 1 fixes the problem and 6470c0d06caSMauro Carvalho Chehab * some other divisions by zero that could happen. 6480c0d06caSMauro Carvalho Chehab */ 6490c0d06caSMauro Carvalho Chehab for (i = 0; i < n; ++i) { 6500c0d06caSMauro Carvalho Chehab interval = get_unaligned_le32(&buffer[26+4*i]); 6510c0d06caSMauro Carvalho Chehab *(*intervals)++ = interval ? interval : 1; 6520c0d06caSMauro Carvalho Chehab } 6530c0d06caSMauro Carvalho Chehab 6540c0d06caSMauro Carvalho Chehab /* Make sure that the default frame interval stays between 6550c0d06caSMauro Carvalho Chehab * the boundaries. 6560c0d06caSMauro Carvalho Chehab */ 6570c0d06caSMauro Carvalho Chehab n -= frame->bFrameIntervalType ? 1 : 2; 6580c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 6590c0d06caSMauro Carvalho Chehab min(frame->dwFrameInterval[n], 6600c0d06caSMauro Carvalho Chehab max(frame->dwFrameInterval[0], 6610c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval)); 6620c0d06caSMauro Carvalho Chehab 6630c0d06caSMauro Carvalho Chehab if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) { 6640c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = 1; 6650c0d06caSMauro Carvalho Chehab frame->dwFrameInterval[0] = 6660c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval; 6670c0d06caSMauro Carvalho Chehab } 6680c0d06caSMauro Carvalho Chehab 6690c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n", 6700c0d06caSMauro Carvalho Chehab frame->wWidth, frame->wHeight, 6710c0d06caSMauro Carvalho Chehab 10000000/frame->dwDefaultFrameInterval, 6720c0d06caSMauro Carvalho Chehab (100000000/frame->dwDefaultFrameInterval)%10); 6730c0d06caSMauro Carvalho Chehab 6740c0d06caSMauro Carvalho Chehab format->nframes++; 6750c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 6760c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 6770c0d06caSMauro Carvalho Chehab } 6780c0d06caSMauro Carvalho Chehab 6790c0d06caSMauro Carvalho Chehab if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 6800c0d06caSMauro Carvalho Chehab buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { 6810c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 6820c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 6830c0d06caSMauro Carvalho Chehab } 6840c0d06caSMauro Carvalho Chehab 6850c0d06caSMauro Carvalho Chehab if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 6860c0d06caSMauro Carvalho Chehab buffer[2] == UVC_VS_COLORFORMAT) { 6870c0d06caSMauro Carvalho Chehab if (buflen < 6) { 6880c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 6890c0d06caSMauro Carvalho Chehab "interface %d COLORFORMAT error\n", 6900c0d06caSMauro Carvalho Chehab dev->udev->devnum, 6910c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 6920c0d06caSMauro Carvalho Chehab return -EINVAL; 6930c0d06caSMauro Carvalho Chehab } 6940c0d06caSMauro Carvalho Chehab 6950c0d06caSMauro Carvalho Chehab format->colorspace = uvc_colorspace(buffer[3]); 6960c0d06caSMauro Carvalho Chehab 6970c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 6980c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 6990c0d06caSMauro Carvalho Chehab } 7000c0d06caSMauro Carvalho Chehab 7010c0d06caSMauro Carvalho Chehab return buffer - start; 7020c0d06caSMauro Carvalho Chehab } 7030c0d06caSMauro Carvalho Chehab 7040c0d06caSMauro Carvalho Chehab static int uvc_parse_streaming(struct uvc_device *dev, 7050c0d06caSMauro Carvalho Chehab struct usb_interface *intf) 7060c0d06caSMauro Carvalho Chehab { 7070c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming = NULL; 7080c0d06caSMauro Carvalho Chehab struct uvc_format *format; 7090c0d06caSMauro Carvalho Chehab struct uvc_frame *frame; 7100c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = &intf->altsetting[0]; 7110c0d06caSMauro Carvalho Chehab unsigned char *_buffer, *buffer = alts->extra; 7120c0d06caSMauro Carvalho Chehab int _buflen, buflen = alts->extralen; 7130c0d06caSMauro Carvalho Chehab unsigned int nformats = 0, nframes = 0, nintervals = 0; 7140c0d06caSMauro Carvalho Chehab unsigned int size, i, n, p; 7152c6b222cSLaurent Pinchart u32 *interval; 7162c6b222cSLaurent Pinchart u16 psize; 7170c0d06caSMauro Carvalho Chehab int ret = -EINVAL; 7180c0d06caSMauro Carvalho Chehab 7190c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass 7200c0d06caSMauro Carvalho Chehab != UVC_SC_VIDEOSTREAMING) { 7210c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a " 7220c0d06caSMauro Carvalho Chehab "video streaming interface\n", dev->udev->devnum, 7230c0d06caSMauro Carvalho Chehab intf->altsetting[0].desc.bInterfaceNumber); 7240c0d06caSMauro Carvalho Chehab return -EINVAL; 7250c0d06caSMauro Carvalho Chehab } 7260c0d06caSMauro Carvalho Chehab 7270c0d06caSMauro Carvalho Chehab if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) { 7280c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already " 7290c0d06caSMauro Carvalho Chehab "claimed\n", dev->udev->devnum, 7300c0d06caSMauro Carvalho Chehab intf->altsetting[0].desc.bInterfaceNumber); 7310c0d06caSMauro Carvalho Chehab return -EINVAL; 7320c0d06caSMauro Carvalho Chehab } 7330c0d06caSMauro Carvalho Chehab 734ece41454SKieran Bingham streaming = uvc_stream_new(dev, intf); 7350c0d06caSMauro Carvalho Chehab if (streaming == NULL) { 7360c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, intf); 737ece41454SKieran Bingham return -ENOMEM; 7380c0d06caSMauro Carvalho Chehab } 7390c0d06caSMauro Carvalho Chehab 7400c0d06caSMauro Carvalho Chehab /* The Pico iMage webcam has its class-specific interface descriptors 7410c0d06caSMauro Carvalho Chehab * after the endpoint descriptors. 7420c0d06caSMauro Carvalho Chehab */ 7430c0d06caSMauro Carvalho Chehab if (buflen == 0) { 7440c0d06caSMauro Carvalho Chehab for (i = 0; i < alts->desc.bNumEndpoints; ++i) { 7450c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep = &alts->endpoint[i]; 7460c0d06caSMauro Carvalho Chehab 7470c0d06caSMauro Carvalho Chehab if (ep->extralen == 0) 7480c0d06caSMauro Carvalho Chehab continue; 7490c0d06caSMauro Carvalho Chehab 7500c0d06caSMauro Carvalho Chehab if (ep->extralen > 2 && 7510c0d06caSMauro Carvalho Chehab ep->extra[1] == USB_DT_CS_INTERFACE) { 7520c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "trying extra data " 7530c0d06caSMauro Carvalho Chehab "from endpoint %u.\n", i); 7540c0d06caSMauro Carvalho Chehab buffer = alts->endpoint[i].extra; 7550c0d06caSMauro Carvalho Chehab buflen = alts->endpoint[i].extralen; 7560c0d06caSMauro Carvalho Chehab break; 7570c0d06caSMauro Carvalho Chehab } 7580c0d06caSMauro Carvalho Chehab } 7590c0d06caSMauro Carvalho Chehab } 7600c0d06caSMauro Carvalho Chehab 7610c0d06caSMauro Carvalho Chehab /* Skip the standard interface descriptors. */ 7620c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) { 7630c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 7640c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 7650c0d06caSMauro Carvalho Chehab } 7660c0d06caSMauro Carvalho Chehab 7670c0d06caSMauro Carvalho Chehab if (buflen <= 2) { 7680c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming " 7690c0d06caSMauro Carvalho Chehab "interface descriptors found.\n"); 7700c0d06caSMauro Carvalho Chehab goto error; 7710c0d06caSMauro Carvalho Chehab } 7720c0d06caSMauro Carvalho Chehab 7730c0d06caSMauro Carvalho Chehab /* Parse the header descriptor. */ 7740c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 7750c0d06caSMauro Carvalho Chehab case UVC_VS_OUTPUT_HEADER: 7760c0d06caSMauro Carvalho Chehab streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 7770c0d06caSMauro Carvalho Chehab size = 9; 7780c0d06caSMauro Carvalho Chehab break; 7790c0d06caSMauro Carvalho Chehab 7800c0d06caSMauro Carvalho Chehab case UVC_VS_INPUT_HEADER: 7810c0d06caSMauro Carvalho Chehab streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 7820c0d06caSMauro Carvalho Chehab size = 13; 7830c0d06caSMauro Carvalho Chehab break; 7840c0d06caSMauro Carvalho Chehab 7850c0d06caSMauro Carvalho Chehab default: 7860c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " 7870c0d06caSMauro Carvalho Chehab "%d HEADER descriptor not found.\n", dev->udev->devnum, 7880c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 7890c0d06caSMauro Carvalho Chehab goto error; 7900c0d06caSMauro Carvalho Chehab } 7910c0d06caSMauro Carvalho Chehab 7920c0d06caSMauro Carvalho Chehab p = buflen >= 4 ? buffer[3] : 0; 7930c0d06caSMauro Carvalho Chehab n = buflen >= size ? buffer[size-1] : 0; 7940c0d06caSMauro Carvalho Chehab 7950c0d06caSMauro Carvalho Chehab if (buflen < size + p*n) { 7960c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 7970c0d06caSMauro Carvalho Chehab "interface %d HEADER descriptor is invalid.\n", 7980c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber); 7990c0d06caSMauro Carvalho Chehab goto error; 8000c0d06caSMauro Carvalho Chehab } 8010c0d06caSMauro Carvalho Chehab 8020c0d06caSMauro Carvalho Chehab streaming->header.bNumFormats = p; 8030c0d06caSMauro Carvalho Chehab streaming->header.bEndpointAddress = buffer[6]; 8040c0d06caSMauro Carvalho Chehab if (buffer[2] == UVC_VS_INPUT_HEADER) { 8050c0d06caSMauro Carvalho Chehab streaming->header.bmInfo = buffer[7]; 8060c0d06caSMauro Carvalho Chehab streaming->header.bTerminalLink = buffer[8]; 8070c0d06caSMauro Carvalho Chehab streaming->header.bStillCaptureMethod = buffer[9]; 8080c0d06caSMauro Carvalho Chehab streaming->header.bTriggerSupport = buffer[10]; 8090c0d06caSMauro Carvalho Chehab streaming->header.bTriggerUsage = buffer[11]; 8100c0d06caSMauro Carvalho Chehab } else { 8110c0d06caSMauro Carvalho Chehab streaming->header.bTerminalLink = buffer[7]; 8120c0d06caSMauro Carvalho Chehab } 8130c0d06caSMauro Carvalho Chehab streaming->header.bControlSize = n; 8140c0d06caSMauro Carvalho Chehab 8150c0d06caSMauro Carvalho Chehab streaming->header.bmaControls = kmemdup(&buffer[size], p * n, 8160c0d06caSMauro Carvalho Chehab GFP_KERNEL); 8170c0d06caSMauro Carvalho Chehab if (streaming->header.bmaControls == NULL) { 8180c0d06caSMauro Carvalho Chehab ret = -ENOMEM; 8190c0d06caSMauro Carvalho Chehab goto error; 8200c0d06caSMauro Carvalho Chehab } 8210c0d06caSMauro Carvalho Chehab 8220c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 8230c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 8240c0d06caSMauro Carvalho Chehab 8250c0d06caSMauro Carvalho Chehab _buffer = buffer; 8260c0d06caSMauro Carvalho Chehab _buflen = buflen; 8270c0d06caSMauro Carvalho Chehab 8280c0d06caSMauro Carvalho Chehab /* Count the format and frame descriptors. */ 8290c0d06caSMauro Carvalho Chehab while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) { 8300c0d06caSMauro Carvalho Chehab switch (_buffer[2]) { 8310c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 8320c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 8330c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 8340c0d06caSMauro Carvalho Chehab nformats++; 8350c0d06caSMauro Carvalho Chehab break; 8360c0d06caSMauro Carvalho Chehab 8370c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 8380c0d06caSMauro Carvalho Chehab /* DV format has no frame descriptor. We will create a 8390c0d06caSMauro Carvalho Chehab * dummy frame descriptor with a dummy frame interval. 8400c0d06caSMauro Carvalho Chehab */ 8410c0d06caSMauro Carvalho Chehab nformats++; 8420c0d06caSMauro Carvalho Chehab nframes++; 8430c0d06caSMauro Carvalho Chehab nintervals++; 8440c0d06caSMauro Carvalho Chehab break; 8450c0d06caSMauro Carvalho Chehab 8460c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MPEG2TS: 8470c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_STREAM_BASED: 8480c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 8490c0d06caSMauro Carvalho Chehab "interface %d FORMAT %u is not supported.\n", 8500c0d06caSMauro Carvalho Chehab dev->udev->devnum, 8510c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, _buffer[2]); 8520c0d06caSMauro Carvalho Chehab break; 8530c0d06caSMauro Carvalho Chehab 8540c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_UNCOMPRESSED: 8550c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_MJPEG: 8560c0d06caSMauro Carvalho Chehab nframes++; 8570c0d06caSMauro Carvalho Chehab if (_buflen > 25) 8580c0d06caSMauro Carvalho Chehab nintervals += _buffer[25] ? _buffer[25] : 3; 8590c0d06caSMauro Carvalho Chehab break; 8600c0d06caSMauro Carvalho Chehab 8610c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_FRAME_BASED: 8620c0d06caSMauro Carvalho Chehab nframes++; 8630c0d06caSMauro Carvalho Chehab if (_buflen > 21) 8640c0d06caSMauro Carvalho Chehab nintervals += _buffer[21] ? _buffer[21] : 3; 8650c0d06caSMauro Carvalho Chehab break; 8660c0d06caSMauro Carvalho Chehab } 8670c0d06caSMauro Carvalho Chehab 8680c0d06caSMauro Carvalho Chehab _buflen -= _buffer[0]; 8690c0d06caSMauro Carvalho Chehab _buffer += _buffer[0]; 8700c0d06caSMauro Carvalho Chehab } 8710c0d06caSMauro Carvalho Chehab 8720c0d06caSMauro Carvalho Chehab if (nformats == 0) { 8730c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " 8740c0d06caSMauro Carvalho Chehab "%d has no supported formats defined.\n", 8750c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber); 8760c0d06caSMauro Carvalho Chehab goto error; 8770c0d06caSMauro Carvalho Chehab } 8780c0d06caSMauro Carvalho Chehab 879f14d4988SLaurent Pinchart size = nformats * sizeof(*format) + nframes * sizeof(*frame) 880f14d4988SLaurent Pinchart + nintervals * sizeof(*interval); 8810c0d06caSMauro Carvalho Chehab format = kzalloc(size, GFP_KERNEL); 8820c0d06caSMauro Carvalho Chehab if (format == NULL) { 8830c0d06caSMauro Carvalho Chehab ret = -ENOMEM; 8840c0d06caSMauro Carvalho Chehab goto error; 8850c0d06caSMauro Carvalho Chehab } 8860c0d06caSMauro Carvalho Chehab 8870c0d06caSMauro Carvalho Chehab frame = (struct uvc_frame *)&format[nformats]; 8882c6b222cSLaurent Pinchart interval = (u32 *)&frame[nframes]; 8890c0d06caSMauro Carvalho Chehab 8900c0d06caSMauro Carvalho Chehab streaming->format = format; 8910c0d06caSMauro Carvalho Chehab streaming->nformats = nformats; 8920c0d06caSMauro Carvalho Chehab 8930c0d06caSMauro Carvalho Chehab /* Parse the format descriptors. */ 8940c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) { 8950c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 8960c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 8970c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 8980c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 8990c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 9000c0d06caSMauro Carvalho Chehab format->frame = frame; 9010c0d06caSMauro Carvalho Chehab ret = uvc_parse_format(dev, streaming, format, 9020c0d06caSMauro Carvalho Chehab &interval, buffer, buflen); 9030c0d06caSMauro Carvalho Chehab if (ret < 0) 9040c0d06caSMauro Carvalho Chehab goto error; 9050c0d06caSMauro Carvalho Chehab 9060c0d06caSMauro Carvalho Chehab frame += format->nframes; 9070c0d06caSMauro Carvalho Chehab format++; 9080c0d06caSMauro Carvalho Chehab 9090c0d06caSMauro Carvalho Chehab buflen -= ret; 9100c0d06caSMauro Carvalho Chehab buffer += ret; 9110c0d06caSMauro Carvalho Chehab continue; 9120c0d06caSMauro Carvalho Chehab 9130c0d06caSMauro Carvalho Chehab default: 9140c0d06caSMauro Carvalho Chehab break; 9150c0d06caSMauro Carvalho Chehab } 9160c0d06caSMauro Carvalho Chehab 9170c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 9180c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 9190c0d06caSMauro Carvalho Chehab } 9200c0d06caSMauro Carvalho Chehab 9210c0d06caSMauro Carvalho Chehab if (buflen) 9220c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " 9230c0d06caSMauro Carvalho Chehab "%d has %u bytes of trailing descriptor garbage.\n", 9240c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber, buflen); 9250c0d06caSMauro Carvalho Chehab 9260c0d06caSMauro Carvalho Chehab /* Parse the alternate settings to find the maximum bandwidth. */ 9270c0d06caSMauro Carvalho Chehab for (i = 0; i < intf->num_altsetting; ++i) { 9280c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep; 9290c0d06caSMauro Carvalho Chehab alts = &intf->altsetting[i]; 9300c0d06caSMauro Carvalho Chehab ep = uvc_find_endpoint(alts, 9310c0d06caSMauro Carvalho Chehab streaming->header.bEndpointAddress); 9320c0d06caSMauro Carvalho Chehab if (ep == NULL) 9330c0d06caSMauro Carvalho Chehab continue; 9340c0d06caSMauro Carvalho Chehab 9350c0d06caSMauro Carvalho Chehab psize = le16_to_cpu(ep->desc.wMaxPacketSize); 9360c0d06caSMauro Carvalho Chehab psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); 9370c0d06caSMauro Carvalho Chehab if (psize > streaming->maxpsize) 9380c0d06caSMauro Carvalho Chehab streaming->maxpsize = psize; 9390c0d06caSMauro Carvalho Chehab } 9400c0d06caSMauro Carvalho Chehab 9410c0d06caSMauro Carvalho Chehab list_add_tail(&streaming->list, &dev->streams); 9420c0d06caSMauro Carvalho Chehab return 0; 9430c0d06caSMauro Carvalho Chehab 9440c0d06caSMauro Carvalho Chehab error: 9450c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, intf); 946ece41454SKieran Bingham uvc_stream_delete(streaming); 9470c0d06caSMauro Carvalho Chehab return ret; 9480c0d06caSMauro Carvalho Chehab } 9490c0d06caSMauro Carvalho Chehab 9500c0d06caSMauro Carvalho Chehab static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, 9510c0d06caSMauro Carvalho Chehab unsigned int num_pads, unsigned int extra_size) 9520c0d06caSMauro Carvalho Chehab { 9530c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 9540c0d06caSMauro Carvalho Chehab unsigned int num_inputs; 9550c0d06caSMauro Carvalho Chehab unsigned int size; 9560c0d06caSMauro Carvalho Chehab unsigned int i; 9570c0d06caSMauro Carvalho Chehab 95889dd34caSNadav Amit extra_size = roundup(extra_size, sizeof(*entity->pads)); 9590c0d06caSMauro Carvalho Chehab num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1; 9600c0d06caSMauro Carvalho Chehab size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads 9610c0d06caSMauro Carvalho Chehab + num_inputs; 9620c0d06caSMauro Carvalho Chehab entity = kzalloc(size, GFP_KERNEL); 9630c0d06caSMauro Carvalho Chehab if (entity == NULL) 9640c0d06caSMauro Carvalho Chehab return NULL; 9650c0d06caSMauro Carvalho Chehab 9660c0d06caSMauro Carvalho Chehab entity->id = id; 9670c0d06caSMauro Carvalho Chehab entity->type = type; 9680c0d06caSMauro Carvalho Chehab 9690c0d06caSMauro Carvalho Chehab entity->num_links = 0; 9700c0d06caSMauro Carvalho Chehab entity->num_pads = num_pads; 9710c0d06caSMauro Carvalho Chehab entity->pads = ((void *)(entity + 1)) + extra_size; 9720c0d06caSMauro Carvalho Chehab 9730c0d06caSMauro Carvalho Chehab for (i = 0; i < num_inputs; ++i) 9740c0d06caSMauro Carvalho Chehab entity->pads[i].flags = MEDIA_PAD_FL_SINK; 9750c0d06caSMauro Carvalho Chehab if (!UVC_ENTITY_IS_OTERM(entity)) 9760c0d06caSMauro Carvalho Chehab entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE; 9770c0d06caSMauro Carvalho Chehab 9780c0d06caSMauro Carvalho Chehab entity->bNrInPins = num_inputs; 9792c6b222cSLaurent Pinchart entity->baSourceID = (u8 *)(&entity->pads[num_pads]); 9800c0d06caSMauro Carvalho Chehab 9810c0d06caSMauro Carvalho Chehab return entity; 9820c0d06caSMauro Carvalho Chehab } 9830c0d06caSMauro Carvalho Chehab 9840c0d06caSMauro Carvalho Chehab /* Parse vendor-specific extensions. */ 9850c0d06caSMauro Carvalho Chehab static int uvc_parse_vendor_control(struct uvc_device *dev, 9860c0d06caSMauro Carvalho Chehab const unsigned char *buffer, int buflen) 9870c0d06caSMauro Carvalho Chehab { 9880c0d06caSMauro Carvalho Chehab struct usb_device *udev = dev->udev; 9890c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 9900c0d06caSMauro Carvalho Chehab struct uvc_entity *unit; 9910c0d06caSMauro Carvalho Chehab unsigned int n, p; 9920c0d06caSMauro Carvalho Chehab int handled = 0; 9930c0d06caSMauro Carvalho Chehab 9940c0d06caSMauro Carvalho Chehab switch (le16_to_cpu(dev->udev->descriptor.idVendor)) { 9950c0d06caSMauro Carvalho Chehab case 0x046d: /* Logitech */ 9960c0d06caSMauro Carvalho Chehab if (buffer[1] != 0x41 || buffer[2] != 0x01) 9970c0d06caSMauro Carvalho Chehab break; 9980c0d06caSMauro Carvalho Chehab 9990c0d06caSMauro Carvalho Chehab /* Logitech implements several vendor specific functions 10000c0d06caSMauro Carvalho Chehab * through vendor specific extension units (LXU). 10010c0d06caSMauro Carvalho Chehab * 10020c0d06caSMauro Carvalho Chehab * The LXU descriptors are similar to XU descriptors 10030c0d06caSMauro Carvalho Chehab * (see "USB Device Video Class for Video Devices", section 10040c0d06caSMauro Carvalho Chehab * 3.7.2.6 "Extension Unit Descriptor") with the following 10050c0d06caSMauro Carvalho Chehab * differences: 10060c0d06caSMauro Carvalho Chehab * 10070c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 10080c0d06caSMauro Carvalho Chehab * 0 bLength 1 Number 10090c0d06caSMauro Carvalho Chehab * Size of this descriptor, in bytes: 24+p+n*2 10100c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 10110c0d06caSMauro Carvalho Chehab * 23+p+n bmControlsType N Bitmap 10120c0d06caSMauro Carvalho Chehab * Individual bits in the set are defined: 10130c0d06caSMauro Carvalho Chehab * 0: Absolute 10140c0d06caSMauro Carvalho Chehab * 1: Relative 10150c0d06caSMauro Carvalho Chehab * 10160c0d06caSMauro Carvalho Chehab * This bitset is mapped exactly the same as bmControls. 10170c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 10180c0d06caSMauro Carvalho Chehab * 23+p+n*2 bReserved 1 Boolean 10190c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 10200c0d06caSMauro Carvalho Chehab * 24+p+n*2 iExtension 1 Index 10210c0d06caSMauro Carvalho Chehab * Index of a string descriptor that describes this 10220c0d06caSMauro Carvalho Chehab * extension unit. 10230c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 10240c0d06caSMauro Carvalho Chehab */ 10250c0d06caSMauro Carvalho Chehab p = buflen >= 22 ? buffer[21] : 0; 10260c0d06caSMauro Carvalho Chehab n = buflen >= 25 + p ? buffer[22+p] : 0; 10270c0d06caSMauro Carvalho Chehab 10280c0d06caSMauro Carvalho Chehab if (buflen < 25 + p + 2*n) { 10290c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 10300c0d06caSMauro Carvalho Chehab "interface %d EXTENSION_UNIT error\n", 10310c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 10320c0d06caSMauro Carvalho Chehab break; 10330c0d06caSMauro Carvalho Chehab } 10340c0d06caSMauro Carvalho Chehab 10350c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3], 10360c0d06caSMauro Carvalho Chehab p + 1, 2*n); 10370c0d06caSMauro Carvalho Chehab if (unit == NULL) 10380c0d06caSMauro Carvalho Chehab return -ENOMEM; 10390c0d06caSMauro Carvalho Chehab 10400c0d06caSMauro Carvalho Chehab memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); 10410c0d06caSMauro Carvalho Chehab unit->extension.bNumControls = buffer[20]; 10420c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[22], p); 10430c0d06caSMauro Carvalho Chehab unit->extension.bControlSize = buffer[22+p]; 10442c6b222cSLaurent Pinchart unit->extension.bmControls = (u8 *)unit + sizeof(*unit); 10452c6b222cSLaurent Pinchart unit->extension.bmControlsType = (u8 *)unit + sizeof(*unit) 10460c0d06caSMauro Carvalho Chehab + n; 10470c0d06caSMauro Carvalho Chehab memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); 10480c0d06caSMauro Carvalho Chehab 10490c0d06caSMauro Carvalho Chehab if (buffer[24+p+2*n] != 0) 10500c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[24+p+2*n], unit->name, 1051f14d4988SLaurent Pinchart sizeof(unit->name)); 10520c0d06caSMauro Carvalho Chehab else 10530c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Extension %u", buffer[3]); 10540c0d06caSMauro Carvalho Chehab 10550c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 10560c0d06caSMauro Carvalho Chehab handled = 1; 10570c0d06caSMauro Carvalho Chehab break; 10580c0d06caSMauro Carvalho Chehab } 10590c0d06caSMauro Carvalho Chehab 10600c0d06caSMauro Carvalho Chehab return handled; 10610c0d06caSMauro Carvalho Chehab } 10620c0d06caSMauro Carvalho Chehab 10630c0d06caSMauro Carvalho Chehab static int uvc_parse_standard_control(struct uvc_device *dev, 10640c0d06caSMauro Carvalho Chehab const unsigned char *buffer, int buflen) 10650c0d06caSMauro Carvalho Chehab { 10660c0d06caSMauro Carvalho Chehab struct usb_device *udev = dev->udev; 10670c0d06caSMauro Carvalho Chehab struct uvc_entity *unit, *term; 10680c0d06caSMauro Carvalho Chehab struct usb_interface *intf; 10690c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 10700c0d06caSMauro Carvalho Chehab unsigned int i, n, p, len; 10712c6b222cSLaurent Pinchart u16 type; 10720c0d06caSMauro Carvalho Chehab 10730c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 10740c0d06caSMauro Carvalho Chehab case UVC_VC_HEADER: 10750c0d06caSMauro Carvalho Chehab n = buflen >= 12 ? buffer[11] : 0; 10760c0d06caSMauro Carvalho Chehab 1077daf41ac2SOliver Neukum if (buflen < 12 + n) { 10780c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 10790c0d06caSMauro Carvalho Chehab "interface %d HEADER error\n", udev->devnum, 10800c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 10810c0d06caSMauro Carvalho Chehab return -EINVAL; 10820c0d06caSMauro Carvalho Chehab } 10830c0d06caSMauro Carvalho Chehab 10840c0d06caSMauro Carvalho Chehab dev->uvc_version = get_unaligned_le16(&buffer[3]); 10850c0d06caSMauro Carvalho Chehab dev->clock_frequency = get_unaligned_le32(&buffer[7]); 10860c0d06caSMauro Carvalho Chehab 10870c0d06caSMauro Carvalho Chehab /* Parse all USB Video Streaming interfaces. */ 10880c0d06caSMauro Carvalho Chehab for (i = 0; i < n; ++i) { 10890c0d06caSMauro Carvalho Chehab intf = usb_ifnum_to_if(udev, buffer[12+i]); 10900c0d06caSMauro Carvalho Chehab if (intf == NULL) { 10910c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d " 10920c0d06caSMauro Carvalho Chehab "interface %d doesn't exists\n", 10930c0d06caSMauro Carvalho Chehab udev->devnum, i); 10940c0d06caSMauro Carvalho Chehab continue; 10950c0d06caSMauro Carvalho Chehab } 10960c0d06caSMauro Carvalho Chehab 10970c0d06caSMauro Carvalho Chehab uvc_parse_streaming(dev, intf); 10980c0d06caSMauro Carvalho Chehab } 10990c0d06caSMauro Carvalho Chehab break; 11000c0d06caSMauro Carvalho Chehab 11010c0d06caSMauro Carvalho Chehab case UVC_VC_INPUT_TERMINAL: 11020c0d06caSMauro Carvalho Chehab if (buflen < 8) { 11030c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 11040c0d06caSMauro Carvalho Chehab "interface %d INPUT_TERMINAL error\n", 11050c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 11060c0d06caSMauro Carvalho Chehab return -EINVAL; 11070c0d06caSMauro Carvalho Chehab } 11080c0d06caSMauro Carvalho Chehab 11090c0d06caSMauro Carvalho Chehab /* Make sure the terminal type MSB is not null, otherwise it 11100c0d06caSMauro Carvalho Chehab * could be confused with a unit. 11110c0d06caSMauro Carvalho Chehab */ 11120c0d06caSMauro Carvalho Chehab type = get_unaligned_le16(&buffer[4]); 11130c0d06caSMauro Carvalho Chehab if ((type & 0xff00) == 0) { 11140c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 11150c0d06caSMauro Carvalho Chehab "interface %d INPUT_TERMINAL %d has invalid " 11160c0d06caSMauro Carvalho Chehab "type 0x%04x, skipping\n", udev->devnum, 11170c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, 11180c0d06caSMauro Carvalho Chehab buffer[3], type); 11190c0d06caSMauro Carvalho Chehab return 0; 11200c0d06caSMauro Carvalho Chehab } 11210c0d06caSMauro Carvalho Chehab 11220c0d06caSMauro Carvalho Chehab n = 0; 11230c0d06caSMauro Carvalho Chehab p = 0; 11240c0d06caSMauro Carvalho Chehab len = 8; 11250c0d06caSMauro Carvalho Chehab 11260c0d06caSMauro Carvalho Chehab if (type == UVC_ITT_CAMERA) { 11270c0d06caSMauro Carvalho Chehab n = buflen >= 15 ? buffer[14] : 0; 11280c0d06caSMauro Carvalho Chehab len = 15; 11290c0d06caSMauro Carvalho Chehab 11300c0d06caSMauro Carvalho Chehab } else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) { 11310c0d06caSMauro Carvalho Chehab n = buflen >= 9 ? buffer[8] : 0; 11320c0d06caSMauro Carvalho Chehab p = buflen >= 10 + n ? buffer[9+n] : 0; 11330c0d06caSMauro Carvalho Chehab len = 10; 11340c0d06caSMauro Carvalho Chehab } 11350c0d06caSMauro Carvalho Chehab 11360c0d06caSMauro Carvalho Chehab if (buflen < len + n + p) { 11370c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 11380c0d06caSMauro Carvalho Chehab "interface %d INPUT_TERMINAL error\n", 11390c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 11400c0d06caSMauro Carvalho Chehab return -EINVAL; 11410c0d06caSMauro Carvalho Chehab } 11420c0d06caSMauro Carvalho Chehab 11430c0d06caSMauro Carvalho Chehab term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3], 11440c0d06caSMauro Carvalho Chehab 1, n + p); 11450c0d06caSMauro Carvalho Chehab if (term == NULL) 11460c0d06caSMauro Carvalho Chehab return -ENOMEM; 11470c0d06caSMauro Carvalho Chehab 11480c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) { 11490c0d06caSMauro Carvalho Chehab term->camera.bControlSize = n; 1150f14d4988SLaurent Pinchart term->camera.bmControls = (u8 *)term + sizeof(*term); 11510c0d06caSMauro Carvalho Chehab term->camera.wObjectiveFocalLengthMin = 11520c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[8]); 11530c0d06caSMauro Carvalho Chehab term->camera.wObjectiveFocalLengthMax = 11540c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[10]); 11550c0d06caSMauro Carvalho Chehab term->camera.wOcularFocalLength = 11560c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[12]); 11570c0d06caSMauro Carvalho Chehab memcpy(term->camera.bmControls, &buffer[15], n); 11580c0d06caSMauro Carvalho Chehab } else if (UVC_ENTITY_TYPE(term) == 11590c0d06caSMauro Carvalho Chehab UVC_ITT_MEDIA_TRANSPORT_INPUT) { 11600c0d06caSMauro Carvalho Chehab term->media.bControlSize = n; 1161f14d4988SLaurent Pinchart term->media.bmControls = (u8 *)term + sizeof(*term); 11620c0d06caSMauro Carvalho Chehab term->media.bTransportModeSize = p; 11632c6b222cSLaurent Pinchart term->media.bmTransportModes = (u8 *)term 1164f14d4988SLaurent Pinchart + sizeof(*term) + n; 11650c0d06caSMauro Carvalho Chehab memcpy(term->media.bmControls, &buffer[9], n); 11660c0d06caSMauro Carvalho Chehab memcpy(term->media.bmTransportModes, &buffer[10+n], p); 11670c0d06caSMauro Carvalho Chehab } 11680c0d06caSMauro Carvalho Chehab 11690c0d06caSMauro Carvalho Chehab if (buffer[7] != 0) 11700c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[7], term->name, 1171f14d4988SLaurent Pinchart sizeof(term->name)); 11720c0d06caSMauro Carvalho Chehab else if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) 11730c0d06caSMauro Carvalho Chehab sprintf(term->name, "Camera %u", buffer[3]); 11740c0d06caSMauro Carvalho Chehab else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT) 11750c0d06caSMauro Carvalho Chehab sprintf(term->name, "Media %u", buffer[3]); 11760c0d06caSMauro Carvalho Chehab else 11770c0d06caSMauro Carvalho Chehab sprintf(term->name, "Input %u", buffer[3]); 11780c0d06caSMauro Carvalho Chehab 11790c0d06caSMauro Carvalho Chehab list_add_tail(&term->list, &dev->entities); 11800c0d06caSMauro Carvalho Chehab break; 11810c0d06caSMauro Carvalho Chehab 11820c0d06caSMauro Carvalho Chehab case UVC_VC_OUTPUT_TERMINAL: 11830c0d06caSMauro Carvalho Chehab if (buflen < 9) { 11840c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 11850c0d06caSMauro Carvalho Chehab "interface %d OUTPUT_TERMINAL error\n", 11860c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 11870c0d06caSMauro Carvalho Chehab return -EINVAL; 11880c0d06caSMauro Carvalho Chehab } 11890c0d06caSMauro Carvalho Chehab 11900c0d06caSMauro Carvalho Chehab /* Make sure the terminal type MSB is not null, otherwise it 11910c0d06caSMauro Carvalho Chehab * could be confused with a unit. 11920c0d06caSMauro Carvalho Chehab */ 11930c0d06caSMauro Carvalho Chehab type = get_unaligned_le16(&buffer[4]); 11940c0d06caSMauro Carvalho Chehab if ((type & 0xff00) == 0) { 11950c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 11960c0d06caSMauro Carvalho Chehab "interface %d OUTPUT_TERMINAL %d has invalid " 11970c0d06caSMauro Carvalho Chehab "type 0x%04x, skipping\n", udev->devnum, 11980c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, buffer[3], type); 11990c0d06caSMauro Carvalho Chehab return 0; 12000c0d06caSMauro Carvalho Chehab } 12010c0d06caSMauro Carvalho Chehab 12020c0d06caSMauro Carvalho Chehab term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3], 12030c0d06caSMauro Carvalho Chehab 1, 0); 12040c0d06caSMauro Carvalho Chehab if (term == NULL) 12050c0d06caSMauro Carvalho Chehab return -ENOMEM; 12060c0d06caSMauro Carvalho Chehab 12070c0d06caSMauro Carvalho Chehab memcpy(term->baSourceID, &buffer[7], 1); 12080c0d06caSMauro Carvalho Chehab 12090c0d06caSMauro Carvalho Chehab if (buffer[8] != 0) 12100c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[8], term->name, 1211f14d4988SLaurent Pinchart sizeof(term->name)); 12120c0d06caSMauro Carvalho Chehab else 12130c0d06caSMauro Carvalho Chehab sprintf(term->name, "Output %u", buffer[3]); 12140c0d06caSMauro Carvalho Chehab 12150c0d06caSMauro Carvalho Chehab list_add_tail(&term->list, &dev->entities); 12160c0d06caSMauro Carvalho Chehab break; 12170c0d06caSMauro Carvalho Chehab 12180c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 12190c0d06caSMauro Carvalho Chehab p = buflen >= 5 ? buffer[4] : 0; 12200c0d06caSMauro Carvalho Chehab 12210c0d06caSMauro Carvalho Chehab if (buflen < 5 || buflen < 6 + p) { 12220c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 12230c0d06caSMauro Carvalho Chehab "interface %d SELECTOR_UNIT error\n", 12240c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 12250c0d06caSMauro Carvalho Chehab return -EINVAL; 12260c0d06caSMauro Carvalho Chehab } 12270c0d06caSMauro Carvalho Chehab 12280c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0); 12290c0d06caSMauro Carvalho Chehab if (unit == NULL) 12300c0d06caSMauro Carvalho Chehab return -ENOMEM; 12310c0d06caSMauro Carvalho Chehab 12320c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[5], p); 12330c0d06caSMauro Carvalho Chehab 12340c0d06caSMauro Carvalho Chehab if (buffer[5+p] != 0) 12350c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[5+p], unit->name, 1236f14d4988SLaurent Pinchart sizeof(unit->name)); 12370c0d06caSMauro Carvalho Chehab else 12380c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Selector %u", buffer[3]); 12390c0d06caSMauro Carvalho Chehab 12400c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 12410c0d06caSMauro Carvalho Chehab break; 12420c0d06caSMauro Carvalho Chehab 12430c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 12440c0d06caSMauro Carvalho Chehab n = buflen >= 8 ? buffer[7] : 0; 12450c0d06caSMauro Carvalho Chehab p = dev->uvc_version >= 0x0110 ? 10 : 9; 12460c0d06caSMauro Carvalho Chehab 12470c0d06caSMauro Carvalho Chehab if (buflen < p + n) { 12480c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 12490c0d06caSMauro Carvalho Chehab "interface %d PROCESSING_UNIT error\n", 12500c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 12510c0d06caSMauro Carvalho Chehab return -EINVAL; 12520c0d06caSMauro Carvalho Chehab } 12530c0d06caSMauro Carvalho Chehab 12540c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n); 12550c0d06caSMauro Carvalho Chehab if (unit == NULL) 12560c0d06caSMauro Carvalho Chehab return -ENOMEM; 12570c0d06caSMauro Carvalho Chehab 12580c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[4], 1); 12590c0d06caSMauro Carvalho Chehab unit->processing.wMaxMultiplier = 12600c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[5]); 12610c0d06caSMauro Carvalho Chehab unit->processing.bControlSize = buffer[7]; 1262f14d4988SLaurent Pinchart unit->processing.bmControls = (u8 *)unit + sizeof(*unit); 12630c0d06caSMauro Carvalho Chehab memcpy(unit->processing.bmControls, &buffer[8], n); 12640c0d06caSMauro Carvalho Chehab if (dev->uvc_version >= 0x0110) 12650c0d06caSMauro Carvalho Chehab unit->processing.bmVideoStandards = buffer[9+n]; 12660c0d06caSMauro Carvalho Chehab 12670c0d06caSMauro Carvalho Chehab if (buffer[8+n] != 0) 12680c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[8+n], unit->name, 1269f14d4988SLaurent Pinchart sizeof(unit->name)); 12700c0d06caSMauro Carvalho Chehab else 12710c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Processing %u", buffer[3]); 12720c0d06caSMauro Carvalho Chehab 12730c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 12740c0d06caSMauro Carvalho Chehab break; 12750c0d06caSMauro Carvalho Chehab 12760c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 12770c0d06caSMauro Carvalho Chehab p = buflen >= 22 ? buffer[21] : 0; 12780c0d06caSMauro Carvalho Chehab n = buflen >= 24 + p ? buffer[22+p] : 0; 12790c0d06caSMauro Carvalho Chehab 12800c0d06caSMauro Carvalho Chehab if (buflen < 24 + p + n) { 12810c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 12820c0d06caSMauro Carvalho Chehab "interface %d EXTENSION_UNIT error\n", 12830c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 12840c0d06caSMauro Carvalho Chehab return -EINVAL; 12850c0d06caSMauro Carvalho Chehab } 12860c0d06caSMauro Carvalho Chehab 12870c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n); 12880c0d06caSMauro Carvalho Chehab if (unit == NULL) 12890c0d06caSMauro Carvalho Chehab return -ENOMEM; 12900c0d06caSMauro Carvalho Chehab 12910c0d06caSMauro Carvalho Chehab memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); 12920c0d06caSMauro Carvalho Chehab unit->extension.bNumControls = buffer[20]; 12930c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[22], p); 12940c0d06caSMauro Carvalho Chehab unit->extension.bControlSize = buffer[22+p]; 1295f14d4988SLaurent Pinchart unit->extension.bmControls = (u8 *)unit + sizeof(*unit); 12960c0d06caSMauro Carvalho Chehab memcpy(unit->extension.bmControls, &buffer[23+p], n); 12970c0d06caSMauro Carvalho Chehab 12980c0d06caSMauro Carvalho Chehab if (buffer[23+p+n] != 0) 12990c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[23+p+n], unit->name, 1300f14d4988SLaurent Pinchart sizeof(unit->name)); 13010c0d06caSMauro Carvalho Chehab else 13020c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Extension %u", buffer[3]); 13030c0d06caSMauro Carvalho Chehab 13040c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 13050c0d06caSMauro Carvalho Chehab break; 13060c0d06caSMauro Carvalho Chehab 13070c0d06caSMauro Carvalho Chehab default: 13080c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE " 13090c0d06caSMauro Carvalho Chehab "descriptor (%u)\n", buffer[2]); 13100c0d06caSMauro Carvalho Chehab break; 13110c0d06caSMauro Carvalho Chehab } 13120c0d06caSMauro Carvalho Chehab 13130c0d06caSMauro Carvalho Chehab return 0; 13140c0d06caSMauro Carvalho Chehab } 13150c0d06caSMauro Carvalho Chehab 13160c0d06caSMauro Carvalho Chehab static int uvc_parse_control(struct uvc_device *dev) 13170c0d06caSMauro Carvalho Chehab { 13180c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 13190c0d06caSMauro Carvalho Chehab unsigned char *buffer = alts->extra; 13200c0d06caSMauro Carvalho Chehab int buflen = alts->extralen; 13210c0d06caSMauro Carvalho Chehab int ret; 13220c0d06caSMauro Carvalho Chehab 13230c0d06caSMauro Carvalho Chehab /* Parse the default alternate setting only, as the UVC specification 13240c0d06caSMauro Carvalho Chehab * defines a single alternate setting, the default alternate setting 13250c0d06caSMauro Carvalho Chehab * zero. 13260c0d06caSMauro Carvalho Chehab */ 13270c0d06caSMauro Carvalho Chehab 13280c0d06caSMauro Carvalho Chehab while (buflen > 2) { 13290c0d06caSMauro Carvalho Chehab if (uvc_parse_vendor_control(dev, buffer, buflen) || 13300c0d06caSMauro Carvalho Chehab buffer[1] != USB_DT_CS_INTERFACE) 13310c0d06caSMauro Carvalho Chehab goto next_descriptor; 13320c0d06caSMauro Carvalho Chehab 13330c0d06caSMauro Carvalho Chehab if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0) 13340c0d06caSMauro Carvalho Chehab return ret; 13350c0d06caSMauro Carvalho Chehab 13360c0d06caSMauro Carvalho Chehab next_descriptor: 13370c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 13380c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 13390c0d06caSMauro Carvalho Chehab } 13400c0d06caSMauro Carvalho Chehab 13410c0d06caSMauro Carvalho Chehab /* Check if the optional status endpoint is present. Built-in iSight 13420c0d06caSMauro Carvalho Chehab * webcams have an interrupt endpoint but spit proprietary data that 13430c0d06caSMauro Carvalho Chehab * don't conform to the UVC status endpoint messages. Don't try to 13440c0d06caSMauro Carvalho Chehab * handle the interrupt endpoint for those cameras. 13450c0d06caSMauro Carvalho Chehab */ 13460c0d06caSMauro Carvalho Chehab if (alts->desc.bNumEndpoints == 1 && 13470c0d06caSMauro Carvalho Chehab !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) { 13480c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep = &alts->endpoint[0]; 13490c0d06caSMauro Carvalho Chehab struct usb_endpoint_descriptor *desc = &ep->desc; 13500c0d06caSMauro Carvalho Chehab 13510c0d06caSMauro Carvalho Chehab if (usb_endpoint_is_int_in(desc) && 13520c0d06caSMauro Carvalho Chehab le16_to_cpu(desc->wMaxPacketSize) >= 8 && 13530c0d06caSMauro Carvalho Chehab desc->bInterval != 0) { 13540c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint " 13550c0d06caSMauro Carvalho Chehab "(addr %02x).\n", desc->bEndpointAddress); 13560c0d06caSMauro Carvalho Chehab dev->int_ep = ep; 13570c0d06caSMauro Carvalho Chehab } 13580c0d06caSMauro Carvalho Chehab } 13590c0d06caSMauro Carvalho Chehab 13600c0d06caSMauro Carvalho Chehab return 0; 13610c0d06caSMauro Carvalho Chehab } 13620c0d06caSMauro Carvalho Chehab 13630c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 13640c0d06caSMauro Carvalho Chehab * UVC device scan 13650c0d06caSMauro Carvalho Chehab */ 13660c0d06caSMauro Carvalho Chehab 13670c0d06caSMauro Carvalho Chehab /* 13680c0d06caSMauro Carvalho Chehab * Scan the UVC descriptors to locate a chain starting at an Output Terminal 13690c0d06caSMauro Carvalho Chehab * and containing the following units: 13700c0d06caSMauro Carvalho Chehab * 13710c0d06caSMauro Carvalho Chehab * - one or more Output Terminals (USB Streaming or Display) 13720c0d06caSMauro Carvalho Chehab * - zero or one Processing Unit 13730c0d06caSMauro Carvalho Chehab * - zero, one or more single-input Selector Units 13740c0d06caSMauro Carvalho Chehab * - zero or one multiple-input Selector Units, provided all inputs are 13750c0d06caSMauro Carvalho Chehab * connected to input terminals 13760c0d06caSMauro Carvalho Chehab * - zero, one or mode single-input Extension Units 13770c0d06caSMauro Carvalho Chehab * - one or more Input Terminals (Camera, External or USB Streaming) 13780c0d06caSMauro Carvalho Chehab * 13790c0d06caSMauro Carvalho Chehab * The terminal and units must match on of the following structures: 13800c0d06caSMauro Carvalho Chehab * 13810c0d06caSMauro Carvalho Chehab * ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0) 13820c0d06caSMauro Carvalho Chehab * ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ... 13830c0d06caSMauro Carvalho Chehab * ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n) 13840c0d06caSMauro Carvalho Chehab * 13850c0d06caSMauro Carvalho Chehab * +---------+ +---------+ -> OTT_*(0) 13860c0d06caSMauro Carvalho Chehab * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ... 13870c0d06caSMauro Carvalho Chehab * +---------+ +---------+ -> OTT_*(n) 13880c0d06caSMauro Carvalho Chehab * 13890c0d06caSMauro Carvalho Chehab * The Processing Unit and Extension Units can be in any order. Additional 13900c0d06caSMauro Carvalho Chehab * Extension Units connected to the main chain as single-unit branches are 13910c0d06caSMauro Carvalho Chehab * also supported. Single-input Selector Units are ignored. 13920c0d06caSMauro Carvalho Chehab */ 13930c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_entity(struct uvc_video_chain *chain, 13940c0d06caSMauro Carvalho Chehab struct uvc_entity *entity) 13950c0d06caSMauro Carvalho Chehab { 13960c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(entity)) { 13970c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 13980c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 139969396c4eSMauro Carvalho Chehab printk(KERN_CONT " <- XU %d", entity->id); 14000c0d06caSMauro Carvalho Chehab 14010c0d06caSMauro Carvalho Chehab if (entity->bNrInPins != 1) { 14020c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more " 14030c0d06caSMauro Carvalho Chehab "than 1 input pin.\n", entity->id); 14040c0d06caSMauro Carvalho Chehab return -1; 14050c0d06caSMauro Carvalho Chehab } 14060c0d06caSMauro Carvalho Chehab 14070c0d06caSMauro Carvalho Chehab break; 14080c0d06caSMauro Carvalho Chehab 14090c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 14100c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 141169396c4eSMauro Carvalho Chehab printk(KERN_CONT " <- PU %d", entity->id); 14120c0d06caSMauro Carvalho Chehab 14130c0d06caSMauro Carvalho Chehab if (chain->processing != NULL) { 14140c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found multiple " 14150c0d06caSMauro Carvalho Chehab "Processing Units in chain.\n"); 14160c0d06caSMauro Carvalho Chehab return -1; 14170c0d06caSMauro Carvalho Chehab } 14180c0d06caSMauro Carvalho Chehab 14190c0d06caSMauro Carvalho Chehab chain->processing = entity; 14200c0d06caSMauro Carvalho Chehab break; 14210c0d06caSMauro Carvalho Chehab 14220c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 14230c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 142469396c4eSMauro Carvalho Chehab printk(KERN_CONT " <- SU %d", entity->id); 14250c0d06caSMauro Carvalho Chehab 14260c0d06caSMauro Carvalho Chehab /* Single-input selector units are ignored. */ 14270c0d06caSMauro Carvalho Chehab if (entity->bNrInPins == 1) 14280c0d06caSMauro Carvalho Chehab break; 14290c0d06caSMauro Carvalho Chehab 14300c0d06caSMauro Carvalho Chehab if (chain->selector != NULL) { 14310c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector " 14320c0d06caSMauro Carvalho Chehab "Units in chain.\n"); 14330c0d06caSMauro Carvalho Chehab return -1; 14340c0d06caSMauro Carvalho Chehab } 14350c0d06caSMauro Carvalho Chehab 14360c0d06caSMauro Carvalho Chehab chain->selector = entity; 14370c0d06caSMauro Carvalho Chehab break; 14380c0d06caSMauro Carvalho Chehab 14390c0d06caSMauro Carvalho Chehab case UVC_ITT_VENDOR_SPECIFIC: 14400c0d06caSMauro Carvalho Chehab case UVC_ITT_CAMERA: 14410c0d06caSMauro Carvalho Chehab case UVC_ITT_MEDIA_TRANSPORT_INPUT: 14420c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 144369396c4eSMauro Carvalho Chehab printk(KERN_CONT " <- IT %d\n", entity->id); 14440c0d06caSMauro Carvalho Chehab 14450c0d06caSMauro Carvalho Chehab break; 14460c0d06caSMauro Carvalho Chehab 14470c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 14480c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 14490c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 14500c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 145169396c4eSMauro Carvalho Chehab printk(KERN_CONT " OT %d", entity->id); 14520c0d06caSMauro Carvalho Chehab 14530c0d06caSMauro Carvalho Chehab break; 14540c0d06caSMauro Carvalho Chehab 14550c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 14560c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_IS_ITERM(entity)) { 14570c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 145869396c4eSMauro Carvalho Chehab printk(KERN_CONT " <- IT %d\n", entity->id); 14590c0d06caSMauro Carvalho Chehab } else { 14600c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 146169396c4eSMauro Carvalho Chehab printk(KERN_CONT " OT %d", entity->id); 14620c0d06caSMauro Carvalho Chehab } 14630c0d06caSMauro Carvalho Chehab 14640c0d06caSMauro Carvalho Chehab break; 14650c0d06caSMauro Carvalho Chehab 14660c0d06caSMauro Carvalho Chehab default: 14670c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type " 14680c0d06caSMauro Carvalho Chehab "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity)); 14690c0d06caSMauro Carvalho Chehab return -1; 14700c0d06caSMauro Carvalho Chehab } 14710c0d06caSMauro Carvalho Chehab 14720c0d06caSMauro Carvalho Chehab list_add_tail(&entity->chain, &chain->entities); 14730c0d06caSMauro Carvalho Chehab return 0; 14740c0d06caSMauro Carvalho Chehab } 14750c0d06caSMauro Carvalho Chehab 14760c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_forward(struct uvc_video_chain *chain, 14770c0d06caSMauro Carvalho Chehab struct uvc_entity *entity, struct uvc_entity *prev) 14780c0d06caSMauro Carvalho Chehab { 14790c0d06caSMauro Carvalho Chehab struct uvc_entity *forward; 14800c0d06caSMauro Carvalho Chehab int found; 14810c0d06caSMauro Carvalho Chehab 14820c0d06caSMauro Carvalho Chehab /* Forward scan */ 14830c0d06caSMauro Carvalho Chehab forward = NULL; 14840c0d06caSMauro Carvalho Chehab found = 0; 14850c0d06caSMauro Carvalho Chehab 14860c0d06caSMauro Carvalho Chehab while (1) { 14870c0d06caSMauro Carvalho Chehab forward = uvc_entity_by_reference(chain->dev, entity->id, 14880c0d06caSMauro Carvalho Chehab forward); 14890c0d06caSMauro Carvalho Chehab if (forward == NULL) 14900c0d06caSMauro Carvalho Chehab break; 14910c0d06caSMauro Carvalho Chehab if (forward == prev) 14920c0d06caSMauro Carvalho Chehab continue; 14930c0d06caSMauro Carvalho Chehab 14940c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(forward)) { 14950c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 14960c0d06caSMauro Carvalho Chehab if (forward->bNrInPins != 1) { 14970c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Extension unit %d " 14980c0d06caSMauro Carvalho Chehab "has more than 1 input pin.\n", 14990c0d06caSMauro Carvalho Chehab entity->id); 15000c0d06caSMauro Carvalho Chehab return -EINVAL; 15010c0d06caSMauro Carvalho Chehab } 15020c0d06caSMauro Carvalho Chehab 15030c0d06caSMauro Carvalho Chehab list_add_tail(&forward->chain, &chain->entities); 15040c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) { 15050c0d06caSMauro Carvalho Chehab if (!found) 150669396c4eSMauro Carvalho Chehab printk(KERN_CONT " (->"); 15070c0d06caSMauro Carvalho Chehab 150869396c4eSMauro Carvalho Chehab printk(KERN_CONT " XU %d", forward->id); 15090c0d06caSMauro Carvalho Chehab found = 1; 15100c0d06caSMauro Carvalho Chehab } 15110c0d06caSMauro Carvalho Chehab break; 15120c0d06caSMauro Carvalho Chehab 15130c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 15140c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 15150c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 15160c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 15170c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_IS_ITERM(forward)) { 15180c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Unsupported input " 15190c0d06caSMauro Carvalho Chehab "terminal %u.\n", forward->id); 15200c0d06caSMauro Carvalho Chehab return -EINVAL; 15210c0d06caSMauro Carvalho Chehab } 15220c0d06caSMauro Carvalho Chehab 15230c0d06caSMauro Carvalho Chehab list_add_tail(&forward->chain, &chain->entities); 15240c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) { 15250c0d06caSMauro Carvalho Chehab if (!found) 152669396c4eSMauro Carvalho Chehab printk(KERN_CONT " (->"); 15270c0d06caSMauro Carvalho Chehab 152869396c4eSMauro Carvalho Chehab printk(KERN_CONT " OT %d", forward->id); 15290c0d06caSMauro Carvalho Chehab found = 1; 15300c0d06caSMauro Carvalho Chehab } 15310c0d06caSMauro Carvalho Chehab break; 15320c0d06caSMauro Carvalho Chehab } 15330c0d06caSMauro Carvalho Chehab } 15340c0d06caSMauro Carvalho Chehab if (found) 153569396c4eSMauro Carvalho Chehab printk(KERN_CONT ")"); 15360c0d06caSMauro Carvalho Chehab 15370c0d06caSMauro Carvalho Chehab return 0; 15380c0d06caSMauro Carvalho Chehab } 15390c0d06caSMauro Carvalho Chehab 15400c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_backward(struct uvc_video_chain *chain, 15410c0d06caSMauro Carvalho Chehab struct uvc_entity **_entity) 15420c0d06caSMauro Carvalho Chehab { 15430c0d06caSMauro Carvalho Chehab struct uvc_entity *entity = *_entity; 15440c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 15450c0d06caSMauro Carvalho Chehab int id = -EINVAL, i; 15460c0d06caSMauro Carvalho Chehab 15470c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(entity)) { 15480c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 15490c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 15500c0d06caSMauro Carvalho Chehab id = entity->baSourceID[0]; 15510c0d06caSMauro Carvalho Chehab break; 15520c0d06caSMauro Carvalho Chehab 15530c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 15540c0d06caSMauro Carvalho Chehab /* Single-input selector units are ignored. */ 15550c0d06caSMauro Carvalho Chehab if (entity->bNrInPins == 1) { 15560c0d06caSMauro Carvalho Chehab id = entity->baSourceID[0]; 15570c0d06caSMauro Carvalho Chehab break; 15580c0d06caSMauro Carvalho Chehab } 15590c0d06caSMauro Carvalho Chehab 15600c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 156169396c4eSMauro Carvalho Chehab printk(KERN_CONT " <- IT"); 15620c0d06caSMauro Carvalho Chehab 15630c0d06caSMauro Carvalho Chehab chain->selector = entity; 15640c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->bNrInPins; ++i) { 15650c0d06caSMauro Carvalho Chehab id = entity->baSourceID[i]; 15660c0d06caSMauro Carvalho Chehab term = uvc_entity_by_id(chain->dev, id); 15670c0d06caSMauro Carvalho Chehab if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { 15680c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Selector unit %d " 15690c0d06caSMauro Carvalho Chehab "input %d isn't connected to an " 15700c0d06caSMauro Carvalho Chehab "input terminal\n", entity->id, i); 15710c0d06caSMauro Carvalho Chehab return -1; 15720c0d06caSMauro Carvalho Chehab } 15730c0d06caSMauro Carvalho Chehab 15740c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 157569396c4eSMauro Carvalho Chehab printk(KERN_CONT " %d", term->id); 15760c0d06caSMauro Carvalho Chehab 15770c0d06caSMauro Carvalho Chehab list_add_tail(&term->chain, &chain->entities); 15780c0d06caSMauro Carvalho Chehab uvc_scan_chain_forward(chain, term, entity); 15790c0d06caSMauro Carvalho Chehab } 15800c0d06caSMauro Carvalho Chehab 15810c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 158269396c4eSMauro Carvalho Chehab printk(KERN_CONT "\n"); 15830c0d06caSMauro Carvalho Chehab 15840c0d06caSMauro Carvalho Chehab id = 0; 15850c0d06caSMauro Carvalho Chehab break; 15860c0d06caSMauro Carvalho Chehab 15870c0d06caSMauro Carvalho Chehab case UVC_ITT_VENDOR_SPECIFIC: 15880c0d06caSMauro Carvalho Chehab case UVC_ITT_CAMERA: 15890c0d06caSMauro Carvalho Chehab case UVC_ITT_MEDIA_TRANSPORT_INPUT: 15900c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 15910c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 15920c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 15930c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 15940c0d06caSMauro Carvalho Chehab id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0; 15950c0d06caSMauro Carvalho Chehab break; 15960c0d06caSMauro Carvalho Chehab } 15970c0d06caSMauro Carvalho Chehab 15980c0d06caSMauro Carvalho Chehab if (id <= 0) { 15990c0d06caSMauro Carvalho Chehab *_entity = NULL; 16000c0d06caSMauro Carvalho Chehab return id; 16010c0d06caSMauro Carvalho Chehab } 16020c0d06caSMauro Carvalho Chehab 16030c0d06caSMauro Carvalho Chehab entity = uvc_entity_by_id(chain->dev, id); 16040c0d06caSMauro Carvalho Chehab if (entity == NULL) { 16050c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found reference to " 16060c0d06caSMauro Carvalho Chehab "unknown entity %d.\n", id); 16070c0d06caSMauro Carvalho Chehab return -EINVAL; 16080c0d06caSMauro Carvalho Chehab } 16090c0d06caSMauro Carvalho Chehab 16100c0d06caSMauro Carvalho Chehab *_entity = entity; 16110c0d06caSMauro Carvalho Chehab return 0; 16120c0d06caSMauro Carvalho Chehab } 16130c0d06caSMauro Carvalho Chehab 16140c0d06caSMauro Carvalho Chehab static int uvc_scan_chain(struct uvc_video_chain *chain, 16150c0d06caSMauro Carvalho Chehab struct uvc_entity *term) 16160c0d06caSMauro Carvalho Chehab { 16170c0d06caSMauro Carvalho Chehab struct uvc_entity *entity, *prev; 16180c0d06caSMauro Carvalho Chehab 16190c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:"); 16200c0d06caSMauro Carvalho Chehab 16210c0d06caSMauro Carvalho Chehab entity = term; 16220c0d06caSMauro Carvalho Chehab prev = NULL; 16230c0d06caSMauro Carvalho Chehab 16240c0d06caSMauro Carvalho Chehab while (entity != NULL) { 16250c0d06caSMauro Carvalho Chehab /* Entity must not be part of an existing chain */ 16260c0d06caSMauro Carvalho Chehab if (entity->chain.next || entity->chain.prev) { 16270c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found reference to " 16280c0d06caSMauro Carvalho Chehab "entity %d already in chain.\n", entity->id); 16290c0d06caSMauro Carvalho Chehab return -EINVAL; 16300c0d06caSMauro Carvalho Chehab } 16310c0d06caSMauro Carvalho Chehab 16320c0d06caSMauro Carvalho Chehab /* Process entity */ 16330c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_entity(chain, entity) < 0) 16340c0d06caSMauro Carvalho Chehab return -EINVAL; 16350c0d06caSMauro Carvalho Chehab 16360c0d06caSMauro Carvalho Chehab /* Forward scan */ 16370c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_forward(chain, entity, prev) < 0) 16380c0d06caSMauro Carvalho Chehab return -EINVAL; 16390c0d06caSMauro Carvalho Chehab 16400c0d06caSMauro Carvalho Chehab /* Backward scan */ 16410c0d06caSMauro Carvalho Chehab prev = entity; 16420c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_backward(chain, &entity) < 0) 16430c0d06caSMauro Carvalho Chehab return -EINVAL; 16440c0d06caSMauro Carvalho Chehab } 16450c0d06caSMauro Carvalho Chehab 16460c0d06caSMauro Carvalho Chehab return 0; 16470c0d06caSMauro Carvalho Chehab } 16480c0d06caSMauro Carvalho Chehab 16490c0d06caSMauro Carvalho Chehab static unsigned int uvc_print_terms(struct list_head *terms, u16 dir, 16500c0d06caSMauro Carvalho Chehab char *buffer) 16510c0d06caSMauro Carvalho Chehab { 16520c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 16530c0d06caSMauro Carvalho Chehab unsigned int nterms = 0; 16540c0d06caSMauro Carvalho Chehab char *p = buffer; 16550c0d06caSMauro Carvalho Chehab 16560c0d06caSMauro Carvalho Chehab list_for_each_entry(term, terms, chain) { 16570c0d06caSMauro Carvalho Chehab if (!UVC_ENTITY_IS_TERM(term) || 16580c0d06caSMauro Carvalho Chehab UVC_TERM_DIRECTION(term) != dir) 16590c0d06caSMauro Carvalho Chehab continue; 16600c0d06caSMauro Carvalho Chehab 16610c0d06caSMauro Carvalho Chehab if (nterms) 16620c0d06caSMauro Carvalho Chehab p += sprintf(p, ","); 16630c0d06caSMauro Carvalho Chehab if (++nterms >= 4) { 16640c0d06caSMauro Carvalho Chehab p += sprintf(p, "..."); 16650c0d06caSMauro Carvalho Chehab break; 16660c0d06caSMauro Carvalho Chehab } 16670c0d06caSMauro Carvalho Chehab p += sprintf(p, "%u", term->id); 16680c0d06caSMauro Carvalho Chehab } 16690c0d06caSMauro Carvalho Chehab 16700c0d06caSMauro Carvalho Chehab return p - buffer; 16710c0d06caSMauro Carvalho Chehab } 16720c0d06caSMauro Carvalho Chehab 16730c0d06caSMauro Carvalho Chehab static const char *uvc_print_chain(struct uvc_video_chain *chain) 16740c0d06caSMauro Carvalho Chehab { 16750c0d06caSMauro Carvalho Chehab static char buffer[43]; 16760c0d06caSMauro Carvalho Chehab char *p = buffer; 16770c0d06caSMauro Carvalho Chehab 16780c0d06caSMauro Carvalho Chehab p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p); 16790c0d06caSMauro Carvalho Chehab p += sprintf(p, " -> "); 16800c0d06caSMauro Carvalho Chehab uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p); 16810c0d06caSMauro Carvalho Chehab 16820c0d06caSMauro Carvalho Chehab return buffer; 16830c0d06caSMauro Carvalho Chehab } 16840c0d06caSMauro Carvalho Chehab 1685e950267aSHenrik Ingo static struct uvc_video_chain *uvc_alloc_chain(struct uvc_device *dev) 1686e950267aSHenrik Ingo { 1687e950267aSHenrik Ingo struct uvc_video_chain *chain; 1688e950267aSHenrik Ingo 1689e950267aSHenrik Ingo chain = kzalloc(sizeof(*chain), GFP_KERNEL); 1690e950267aSHenrik Ingo if (chain == NULL) 1691e950267aSHenrik Ingo return NULL; 1692e950267aSHenrik Ingo 1693e950267aSHenrik Ingo INIT_LIST_HEAD(&chain->entities); 1694e950267aSHenrik Ingo mutex_init(&chain->ctrl_mutex); 1695e950267aSHenrik Ingo chain->dev = dev; 1696e950267aSHenrik Ingo v4l2_prio_init(&chain->prio); 1697e950267aSHenrik Ingo 1698e950267aSHenrik Ingo return chain; 1699e950267aSHenrik Ingo } 1700e950267aSHenrik Ingo 1701e950267aSHenrik Ingo /* 1702e950267aSHenrik Ingo * Fallback heuristic for devices that don't connect units and terminals in a 1703e950267aSHenrik Ingo * valid chain. 1704e950267aSHenrik Ingo * 1705e950267aSHenrik Ingo * Some devices have invalid baSourceID references, causing uvc_scan_chain() 1706e950267aSHenrik Ingo * to fail, but if we just take the entities we can find and put them together 1707e950267aSHenrik Ingo * in the most sensible chain we can think of, turns out they do work anyway. 1708e950267aSHenrik Ingo * Note: This heuristic assumes there is a single chain. 1709e950267aSHenrik Ingo * 1710e950267aSHenrik Ingo * At the time of writing, devices known to have such a broken chain are 1711e950267aSHenrik Ingo * - Acer Integrated Camera (5986:055a) 1712e950267aSHenrik Ingo * - Realtek rtl157a7 (0bda:57a7) 1713e950267aSHenrik Ingo */ 1714e950267aSHenrik Ingo static int uvc_scan_fallback(struct uvc_device *dev) 1715e950267aSHenrik Ingo { 1716e950267aSHenrik Ingo struct uvc_video_chain *chain; 1717e950267aSHenrik Ingo struct uvc_entity *iterm = NULL; 1718e950267aSHenrik Ingo struct uvc_entity *oterm = NULL; 1719e950267aSHenrik Ingo struct uvc_entity *entity; 1720e950267aSHenrik Ingo struct uvc_entity *prev; 1721e950267aSHenrik Ingo 1722e950267aSHenrik Ingo /* 1723e950267aSHenrik Ingo * Start by locating the input and output terminals. We only support 1724e950267aSHenrik Ingo * devices with exactly one of each for now. 1725e950267aSHenrik Ingo */ 1726e950267aSHenrik Ingo list_for_each_entry(entity, &dev->entities, list) { 1727e950267aSHenrik Ingo if (UVC_ENTITY_IS_ITERM(entity)) { 1728e950267aSHenrik Ingo if (iterm) 1729e950267aSHenrik Ingo return -EINVAL; 1730e950267aSHenrik Ingo iterm = entity; 1731e950267aSHenrik Ingo } 1732e950267aSHenrik Ingo 1733e950267aSHenrik Ingo if (UVC_ENTITY_IS_OTERM(entity)) { 1734e950267aSHenrik Ingo if (oterm) 1735e950267aSHenrik Ingo return -EINVAL; 1736e950267aSHenrik Ingo oterm = entity; 1737e950267aSHenrik Ingo } 1738e950267aSHenrik Ingo } 1739e950267aSHenrik Ingo 1740e950267aSHenrik Ingo if (iterm == NULL || oterm == NULL) 1741e950267aSHenrik Ingo return -EINVAL; 1742e950267aSHenrik Ingo 1743e950267aSHenrik Ingo /* Allocate the chain and fill it. */ 1744e950267aSHenrik Ingo chain = uvc_alloc_chain(dev); 1745e950267aSHenrik Ingo if (chain == NULL) 1746e950267aSHenrik Ingo return -ENOMEM; 1747e950267aSHenrik Ingo 1748e950267aSHenrik Ingo if (uvc_scan_chain_entity(chain, oterm) < 0) 1749e950267aSHenrik Ingo goto error; 1750e950267aSHenrik Ingo 1751e950267aSHenrik Ingo prev = oterm; 1752e950267aSHenrik Ingo 1753e950267aSHenrik Ingo /* 1754e950267aSHenrik Ingo * Add all Processing and Extension Units with two pads. The order 1755e950267aSHenrik Ingo * doesn't matter much, use reverse list traversal to connect units in 1756e950267aSHenrik Ingo * UVC descriptor order as we build the chain from output to input. This 1757e950267aSHenrik Ingo * leads to units appearing in the order meant by the manufacturer for 1758e950267aSHenrik Ingo * the cameras known to require this heuristic. 1759e950267aSHenrik Ingo */ 1760e950267aSHenrik Ingo list_for_each_entry_reverse(entity, &dev->entities, list) { 1761e950267aSHenrik Ingo if (entity->type != UVC_VC_PROCESSING_UNIT && 1762e950267aSHenrik Ingo entity->type != UVC_VC_EXTENSION_UNIT) 1763e950267aSHenrik Ingo continue; 1764e950267aSHenrik Ingo 1765e950267aSHenrik Ingo if (entity->num_pads != 2) 1766e950267aSHenrik Ingo continue; 1767e950267aSHenrik Ingo 1768e950267aSHenrik Ingo if (uvc_scan_chain_entity(chain, entity) < 0) 1769e950267aSHenrik Ingo goto error; 1770e950267aSHenrik Ingo 1771e950267aSHenrik Ingo prev->baSourceID[0] = entity->id; 1772e950267aSHenrik Ingo prev = entity; 1773e950267aSHenrik Ingo } 1774e950267aSHenrik Ingo 1775e950267aSHenrik Ingo if (uvc_scan_chain_entity(chain, iterm) < 0) 1776e950267aSHenrik Ingo goto error; 1777e950267aSHenrik Ingo 1778e950267aSHenrik Ingo prev->baSourceID[0] = iterm->id; 1779e950267aSHenrik Ingo 1780e950267aSHenrik Ingo list_add_tail(&chain->list, &dev->chains); 1781e950267aSHenrik Ingo 1782e950267aSHenrik Ingo uvc_trace(UVC_TRACE_PROBE, 1783e950267aSHenrik Ingo "Found a video chain by fallback heuristic (%s).\n", 1784e950267aSHenrik Ingo uvc_print_chain(chain)); 1785e950267aSHenrik Ingo 1786e950267aSHenrik Ingo return 0; 1787e950267aSHenrik Ingo 1788e950267aSHenrik Ingo error: 1789e950267aSHenrik Ingo kfree(chain); 1790e950267aSHenrik Ingo return -EINVAL; 1791e950267aSHenrik Ingo } 1792e950267aSHenrik Ingo 17930c0d06caSMauro Carvalho Chehab /* 17940c0d06caSMauro Carvalho Chehab * Scan the device for video chains and register video devices. 17950c0d06caSMauro Carvalho Chehab * 17960c0d06caSMauro Carvalho Chehab * Chains are scanned starting at their output terminals and walked backwards. 17970c0d06caSMauro Carvalho Chehab */ 17980c0d06caSMauro Carvalho Chehab static int uvc_scan_device(struct uvc_device *dev) 17990c0d06caSMauro Carvalho Chehab { 18000c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 18010c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 18020c0d06caSMauro Carvalho Chehab 18030c0d06caSMauro Carvalho Chehab list_for_each_entry(term, &dev->entities, list) { 18040c0d06caSMauro Carvalho Chehab if (!UVC_ENTITY_IS_OTERM(term)) 18050c0d06caSMauro Carvalho Chehab continue; 18060c0d06caSMauro Carvalho Chehab 18070c0d06caSMauro Carvalho Chehab /* If the terminal is already included in a chain, skip it. 18080c0d06caSMauro Carvalho Chehab * This can happen for chains that have multiple output 18090c0d06caSMauro Carvalho Chehab * terminals, where all output terminals beside the first one 18100c0d06caSMauro Carvalho Chehab * will be inserted in the chain in forward scans. 18110c0d06caSMauro Carvalho Chehab */ 18120c0d06caSMauro Carvalho Chehab if (term->chain.next || term->chain.prev) 18130c0d06caSMauro Carvalho Chehab continue; 18140c0d06caSMauro Carvalho Chehab 1815e950267aSHenrik Ingo chain = uvc_alloc_chain(dev); 18160c0d06caSMauro Carvalho Chehab if (chain == NULL) 18170c0d06caSMauro Carvalho Chehab return -ENOMEM; 18180c0d06caSMauro Carvalho Chehab 18198be8ec6eSLaurent Pinchart term->flags |= UVC_ENTITY_FLAG_DEFAULT; 18208be8ec6eSLaurent Pinchart 18210c0d06caSMauro Carvalho Chehab if (uvc_scan_chain(chain, term) < 0) { 18220c0d06caSMauro Carvalho Chehab kfree(chain); 18230c0d06caSMauro Carvalho Chehab continue; 18240c0d06caSMauro Carvalho Chehab } 18250c0d06caSMauro Carvalho Chehab 18260c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n", 18270c0d06caSMauro Carvalho Chehab uvc_print_chain(chain)); 18280c0d06caSMauro Carvalho Chehab 18290c0d06caSMauro Carvalho Chehab list_add_tail(&chain->list, &dev->chains); 18300c0d06caSMauro Carvalho Chehab } 18310c0d06caSMauro Carvalho Chehab 1832e950267aSHenrik Ingo if (list_empty(&dev->chains)) 1833e950267aSHenrik Ingo uvc_scan_fallback(dev); 1834e950267aSHenrik Ingo 18350c0d06caSMauro Carvalho Chehab if (list_empty(&dev->chains)) { 18360c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "No valid video chain found.\n"); 18370c0d06caSMauro Carvalho Chehab return -1; 18380c0d06caSMauro Carvalho Chehab } 18390c0d06caSMauro Carvalho Chehab 18400c0d06caSMauro Carvalho Chehab return 0; 18410c0d06caSMauro Carvalho Chehab } 18420c0d06caSMauro Carvalho Chehab 18430c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 18440c0d06caSMauro Carvalho Chehab * Video device registration and unregistration 18450c0d06caSMauro Carvalho Chehab */ 18460c0d06caSMauro Carvalho Chehab 18470c0d06caSMauro Carvalho Chehab /* 18480c0d06caSMauro Carvalho Chehab * Delete the UVC device. 18490c0d06caSMauro Carvalho Chehab * 18500c0d06caSMauro Carvalho Chehab * Called by the kernel when the last reference to the uvc_device structure 18510c0d06caSMauro Carvalho Chehab * is released. 18520c0d06caSMauro Carvalho Chehab * 18530c0d06caSMauro Carvalho Chehab * As this function is called after or during disconnect(), all URBs have 1854ece41454SKieran Bingham * already been cancelled by the USB core. There is no need to kill the 18550c0d06caSMauro Carvalho Chehab * interrupt URB manually. 18560c0d06caSMauro Carvalho Chehab */ 18579d15cd95SGuennadi Liakhovetski static void uvc_delete(struct kref *kref) 18580c0d06caSMauro Carvalho Chehab { 18599d15cd95SGuennadi Liakhovetski struct uvc_device *dev = container_of(kref, struct uvc_device, ref); 18600c0d06caSMauro Carvalho Chehab struct list_head *p, *n; 18610c0d06caSMauro Carvalho Chehab 18620c0d06caSMauro Carvalho Chehab uvc_status_cleanup(dev); 18630c0d06caSMauro Carvalho Chehab uvc_ctrl_cleanup_device(dev); 18640c0d06caSMauro Carvalho Chehab 18652228d80dSTakashi Iwai usb_put_intf(dev->intf); 18662228d80dSTakashi Iwai usb_put_dev(dev->udev); 18672228d80dSTakashi Iwai 18680c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 18699832e155SJavier Martinez Canillas media_device_cleanup(&dev->mdev); 18700c0d06caSMauro Carvalho Chehab #endif 18710c0d06caSMauro Carvalho Chehab 18720c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->chains) { 18730c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 18740c0d06caSMauro Carvalho Chehab chain = list_entry(p, struct uvc_video_chain, list); 18750c0d06caSMauro Carvalho Chehab kfree(chain); 18760c0d06caSMauro Carvalho Chehab } 18770c0d06caSMauro Carvalho Chehab 18780c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->entities) { 18790c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 18800c0d06caSMauro Carvalho Chehab entity = list_entry(p, struct uvc_entity, list); 18810c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 18820c0d06caSMauro Carvalho Chehab uvc_mc_cleanup_entity(entity); 18830c0d06caSMauro Carvalho Chehab #endif 18840c0d06caSMauro Carvalho Chehab kfree(entity); 18850c0d06caSMauro Carvalho Chehab } 18860c0d06caSMauro Carvalho Chehab 18870c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->streams) { 18880c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming; 18890c0d06caSMauro Carvalho Chehab streaming = list_entry(p, struct uvc_streaming, list); 18900c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, 18910c0d06caSMauro Carvalho Chehab streaming->intf); 1892ece41454SKieran Bingham uvc_stream_delete(streaming); 18930c0d06caSMauro Carvalho Chehab } 18940c0d06caSMauro Carvalho Chehab 18950c0d06caSMauro Carvalho Chehab kfree(dev); 18960c0d06caSMauro Carvalho Chehab } 18970c0d06caSMauro Carvalho Chehab 18980c0d06caSMauro Carvalho Chehab static void uvc_release(struct video_device *vdev) 18990c0d06caSMauro Carvalho Chehab { 19000c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream = video_get_drvdata(vdev); 19010c0d06caSMauro Carvalho Chehab struct uvc_device *dev = stream->dev; 19020c0d06caSMauro Carvalho Chehab 19039d15cd95SGuennadi Liakhovetski kref_put(&dev->ref, uvc_delete); 19040c0d06caSMauro Carvalho Chehab } 19050c0d06caSMauro Carvalho Chehab 19060c0d06caSMauro Carvalho Chehab /* 19070c0d06caSMauro Carvalho Chehab * Unregister the video devices. 19080c0d06caSMauro Carvalho Chehab */ 19090c0d06caSMauro Carvalho Chehab static void uvc_unregister_video(struct uvc_device *dev) 19100c0d06caSMauro Carvalho Chehab { 19110c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 19120c0d06caSMauro Carvalho Chehab 19130c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 1914d8da7513SHans Verkuil if (!video_is_registered(&stream->vdev)) 19150c0d06caSMauro Carvalho Chehab continue; 19160c0d06caSMauro Carvalho Chehab 1917d8da7513SHans Verkuil video_unregister_device(&stream->vdev); 1918088ead25SGuennadi Liakhovetski video_unregister_device(&stream->meta.vdev); 19190c0d06caSMauro Carvalho Chehab 19200c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup_stream(stream); 19210c0d06caSMauro Carvalho Chehab } 192210e1fdb9SDaniel Axtens 192310e1fdb9SDaniel Axtens uvc_status_unregister(dev); 192410e1fdb9SDaniel Axtens 192510e1fdb9SDaniel Axtens if (dev->vdev.dev) 192610e1fdb9SDaniel Axtens v4l2_device_unregister(&dev->vdev); 192710e1fdb9SDaniel Axtens #ifdef CONFIG_MEDIA_CONTROLLER 192810e1fdb9SDaniel Axtens if (media_devnode_is_registered(dev->mdev.devnode)) 192910e1fdb9SDaniel Axtens media_device_unregister(&dev->mdev); 193010e1fdb9SDaniel Axtens #endif 19310c0d06caSMauro Carvalho Chehab } 19320c0d06caSMauro Carvalho Chehab 193331a96f4cSLaurent Pinchart int uvc_register_video_device(struct uvc_device *dev, 193431a96f4cSLaurent Pinchart struct uvc_streaming *stream, 193531a96f4cSLaurent Pinchart struct video_device *vdev, 193631a96f4cSLaurent Pinchart struct uvc_video_queue *queue, 193731a96f4cSLaurent Pinchart enum v4l2_buf_type type, 193831a96f4cSLaurent Pinchart const struct v4l2_file_operations *fops, 193931a96f4cSLaurent Pinchart const struct v4l2_ioctl_ops *ioctl_ops) 19400c0d06caSMauro Carvalho Chehab { 19410c0d06caSMauro Carvalho Chehab int ret; 19420c0d06caSMauro Carvalho Chehab 1943b83bba24SLaurent Pinchart /* Initialize the video buffers queue. */ 194431a96f4cSLaurent Pinchart ret = uvc_queue_init(queue, type, !uvc_no_drop_param); 1945b83bba24SLaurent Pinchart if (ret) 1946b83bba24SLaurent Pinchart return ret; 1947b83bba24SLaurent Pinchart 19480c0d06caSMauro Carvalho Chehab /* Register the device with V4L. */ 19490c0d06caSMauro Carvalho Chehab 195031a96f4cSLaurent Pinchart /* 195131a96f4cSLaurent Pinchart * We already hold a reference to dev->udev. The video device will be 19520c0d06caSMauro Carvalho Chehab * unregistered before the reference is released, so we don't need to 19530c0d06caSMauro Carvalho Chehab * get another one. 19540c0d06caSMauro Carvalho Chehab */ 19550c0d06caSMauro Carvalho Chehab vdev->v4l2_dev = &dev->vdev; 195631a96f4cSLaurent Pinchart vdev->fops = fops; 195731a96f4cSLaurent Pinchart vdev->ioctl_ops = ioctl_ops; 19580c0d06caSMauro Carvalho Chehab vdev->release = uvc_release; 19590550513cSLaurent Pinchart vdev->prio = &stream->chain->prio; 196031a96f4cSLaurent Pinchart if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 1961954f340fSHans Verkuil vdev->vfl_dir = VFL_DIR_TX; 196231a96f4cSLaurent Pinchart else 196331a96f4cSLaurent Pinchart vdev->vfl_dir = VFL_DIR_RX; 196494c53e26SLaurent Pinchart 196594c53e26SLaurent Pinchart switch (type) { 196694c53e26SLaurent Pinchart case V4L2_BUF_TYPE_VIDEO_CAPTURE: 196794c53e26SLaurent Pinchart default: 196894c53e26SLaurent Pinchart vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; 196994c53e26SLaurent Pinchart break; 197094c53e26SLaurent Pinchart case V4L2_BUF_TYPE_VIDEO_OUTPUT: 197194c53e26SLaurent Pinchart vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; 197294c53e26SLaurent Pinchart break; 1973088ead25SGuennadi Liakhovetski case V4L2_BUF_TYPE_META_CAPTURE: 1974088ead25SGuennadi Liakhovetski vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING; 1975088ead25SGuennadi Liakhovetski break; 197694c53e26SLaurent Pinchart } 197794c53e26SLaurent Pinchart 1978c0decac1SMauro Carvalho Chehab strscpy(vdev->name, dev->name, sizeof(vdev->name)); 19790c0d06caSMauro Carvalho Chehab 198031a96f4cSLaurent Pinchart /* 198131a96f4cSLaurent Pinchart * Set the driver data before calling video_register_device, otherwise 198231a96f4cSLaurent Pinchart * the file open() handler might race us. 19830c0d06caSMauro Carvalho Chehab */ 19840c0d06caSMauro Carvalho Chehab video_set_drvdata(vdev, stream); 19850c0d06caSMauro Carvalho Chehab 19860c0d06caSMauro Carvalho Chehab ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); 19870c0d06caSMauro Carvalho Chehab if (ret < 0) { 198831a96f4cSLaurent Pinchart uvc_printk(KERN_ERR, "Failed to register %s device (%d).\n", 198931a96f4cSLaurent Pinchart v4l2_type_names[type], ret); 199031a96f4cSLaurent Pinchart return ret; 199131a96f4cSLaurent Pinchart } 199231a96f4cSLaurent Pinchart 199331a96f4cSLaurent Pinchart kref_get(&dev->ref); 199431a96f4cSLaurent Pinchart return 0; 199531a96f4cSLaurent Pinchart } 199631a96f4cSLaurent Pinchart 199731a96f4cSLaurent Pinchart static int uvc_register_video(struct uvc_device *dev, 199831a96f4cSLaurent Pinchart struct uvc_streaming *stream) 199931a96f4cSLaurent Pinchart { 200031a96f4cSLaurent Pinchart int ret; 200131a96f4cSLaurent Pinchart 200231a96f4cSLaurent Pinchart /* Initialize the streaming interface with default parameters. */ 200331a96f4cSLaurent Pinchart ret = uvc_video_init(stream); 200431a96f4cSLaurent Pinchart if (ret < 0) { 200531a96f4cSLaurent Pinchart uvc_printk(KERN_ERR, "Failed to initialize the device (%d).\n", 20060c0d06caSMauro Carvalho Chehab ret); 20070c0d06caSMauro Carvalho Chehab return ret; 20080c0d06caSMauro Carvalho Chehab } 20090c0d06caSMauro Carvalho Chehab 2010f887e99aSLaurent Pinchart if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 2011088ead25SGuennadi Liakhovetski stream->chain->caps |= V4L2_CAP_VIDEO_CAPTURE 2012088ead25SGuennadi Liakhovetski | V4L2_CAP_META_CAPTURE; 2013f887e99aSLaurent Pinchart else 2014f887e99aSLaurent Pinchart stream->chain->caps |= V4L2_CAP_VIDEO_OUTPUT; 2015f887e99aSLaurent Pinchart 201631a96f4cSLaurent Pinchart uvc_debugfs_init_stream(stream); 201731a96f4cSLaurent Pinchart 201831a96f4cSLaurent Pinchart /* Register the device with V4L. */ 201931a96f4cSLaurent Pinchart return uvc_register_video_device(dev, stream, &stream->vdev, 202031a96f4cSLaurent Pinchart &stream->queue, stream->type, 202131a96f4cSLaurent Pinchart &uvc_fops, &uvc_ioctl_ops); 20220c0d06caSMauro Carvalho Chehab } 20230c0d06caSMauro Carvalho Chehab 20240c0d06caSMauro Carvalho Chehab /* 20250c0d06caSMauro Carvalho Chehab * Register all video devices in all chains. 20260c0d06caSMauro Carvalho Chehab */ 20270c0d06caSMauro Carvalho Chehab static int uvc_register_terms(struct uvc_device *dev, 20280c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain) 20290c0d06caSMauro Carvalho Chehab { 20300c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 20310c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 20320c0d06caSMauro Carvalho Chehab int ret; 20330c0d06caSMauro Carvalho Chehab 20340c0d06caSMauro Carvalho Chehab list_for_each_entry(term, &chain->entities, chain) { 20350c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) 20360c0d06caSMauro Carvalho Chehab continue; 20370c0d06caSMauro Carvalho Chehab 20380c0d06caSMauro Carvalho Chehab stream = uvc_stream_by_id(dev, term->id); 20390c0d06caSMauro Carvalho Chehab if (stream == NULL) { 20400c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "No streaming interface found " 20410c0d06caSMauro Carvalho Chehab "for terminal %u.", term->id); 20420c0d06caSMauro Carvalho Chehab continue; 20430c0d06caSMauro Carvalho Chehab } 20440c0d06caSMauro Carvalho Chehab 20450c0d06caSMauro Carvalho Chehab stream->chain = chain; 20460c0d06caSMauro Carvalho Chehab ret = uvc_register_video(dev, stream); 20470c0d06caSMauro Carvalho Chehab if (ret < 0) 20480c0d06caSMauro Carvalho Chehab return ret; 20490c0d06caSMauro Carvalho Chehab 2050088ead25SGuennadi Liakhovetski /* Register a metadata node, but ignore a possible failure, 2051088ead25SGuennadi Liakhovetski * complete registration of video nodes anyway. 2052088ead25SGuennadi Liakhovetski */ 2053088ead25SGuennadi Liakhovetski uvc_meta_register(stream); 2054088ead25SGuennadi Liakhovetski 2055d8da7513SHans Verkuil term->vdev = &stream->vdev; 20560c0d06caSMauro Carvalho Chehab } 20570c0d06caSMauro Carvalho Chehab 20580c0d06caSMauro Carvalho Chehab return 0; 20590c0d06caSMauro Carvalho Chehab } 20600c0d06caSMauro Carvalho Chehab 20610c0d06caSMauro Carvalho Chehab static int uvc_register_chains(struct uvc_device *dev) 20620c0d06caSMauro Carvalho Chehab { 20630c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 20640c0d06caSMauro Carvalho Chehab int ret; 20650c0d06caSMauro Carvalho Chehab 20660c0d06caSMauro Carvalho Chehab list_for_each_entry(chain, &dev->chains, list) { 20670c0d06caSMauro Carvalho Chehab ret = uvc_register_terms(dev, chain); 20680c0d06caSMauro Carvalho Chehab if (ret < 0) 20690c0d06caSMauro Carvalho Chehab return ret; 20700c0d06caSMauro Carvalho Chehab 20710c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 20720c0d06caSMauro Carvalho Chehab ret = uvc_mc_register_entities(chain); 20736689df06SColin Ian King if (ret < 0) 20746689df06SColin Ian King uvc_printk(KERN_INFO, 20756689df06SColin Ian King "Failed to register entities (%d).\n", ret); 20760c0d06caSMauro Carvalho Chehab #endif 20770c0d06caSMauro Carvalho Chehab } 20780c0d06caSMauro Carvalho Chehab 20790c0d06caSMauro Carvalho Chehab return 0; 20800c0d06caSMauro Carvalho Chehab } 20810c0d06caSMauro Carvalho Chehab 20820c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 20830c0d06caSMauro Carvalho Chehab * USB probe, disconnect, suspend and resume 20840c0d06caSMauro Carvalho Chehab */ 20850c0d06caSMauro Carvalho Chehab 20863a03284dSLaurent Pinchart static const struct uvc_device_info uvc_quirk_none = { 0 }; 20873bc85817SGuennadi Liakhovetski 20880c0d06caSMauro Carvalho Chehab static int uvc_probe(struct usb_interface *intf, 20890c0d06caSMauro Carvalho Chehab const struct usb_device_id *id) 20900c0d06caSMauro Carvalho Chehab { 20910c0d06caSMauro Carvalho Chehab struct usb_device *udev = interface_to_usbdev(intf); 20920c0d06caSMauro Carvalho Chehab struct uvc_device *dev; 20933bc85817SGuennadi Liakhovetski const struct uvc_device_info *info = 20943bc85817SGuennadi Liakhovetski (const struct uvc_device_info *)id->driver_info; 2095e7b09f18SPeter Boström int function; 20960c0d06caSMauro Carvalho Chehab int ret; 20970c0d06caSMauro Carvalho Chehab 20980c0d06caSMauro Carvalho Chehab if (id->idVendor && id->idProduct) 20990c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s " 21000c0d06caSMauro Carvalho Chehab "(%04x:%04x)\n", udev->devpath, id->idVendor, 21010c0d06caSMauro Carvalho Chehab id->idProduct); 21020c0d06caSMauro Carvalho Chehab else 21030c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n", 21040c0d06caSMauro Carvalho Chehab udev->devpath); 21050c0d06caSMauro Carvalho Chehab 21060c0d06caSMauro Carvalho Chehab /* Allocate memory for the device and initialize it. */ 2107f14d4988SLaurent Pinchart dev = kzalloc(sizeof(*dev), GFP_KERNEL); 2108f14d4988SLaurent Pinchart if (dev == NULL) 21090c0d06caSMauro Carvalho Chehab return -ENOMEM; 21100c0d06caSMauro Carvalho Chehab 21110c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->entities); 21120c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->chains); 21130c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->streams); 21149d15cd95SGuennadi Liakhovetski kref_init(&dev->ref); 21150c0d06caSMauro Carvalho Chehab atomic_set(&dev->nmappings, 0); 211617706f56SLaurent Pinchart mutex_init(&dev->lock); 21170c0d06caSMauro Carvalho Chehab 21180c0d06caSMauro Carvalho Chehab dev->udev = usb_get_dev(udev); 21190c0d06caSMauro Carvalho Chehab dev->intf = usb_get_intf(intf); 21200c0d06caSMauro Carvalho Chehab dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; 21213a03284dSLaurent Pinchart dev->info = info ? info : &uvc_quirk_none; 21223a03284dSLaurent Pinchart dev->quirks = uvc_quirks_param == -1 21233a03284dSLaurent Pinchart ? dev->info->quirks : uvc_quirks_param; 21240c0d06caSMauro Carvalho Chehab 21250c0d06caSMauro Carvalho Chehab if (udev->product != NULL) 2126c0decac1SMauro Carvalho Chehab strscpy(dev->name, udev->product, sizeof(dev->name)); 21270c0d06caSMauro Carvalho Chehab else 2128f14d4988SLaurent Pinchart snprintf(dev->name, sizeof(dev->name), 21290c0d06caSMauro Carvalho Chehab "UVC Camera (%04x:%04x)", 21300c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idVendor), 21310c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idProduct)); 21320c0d06caSMauro Carvalho Chehab 2133e7b09f18SPeter Boström /* 2134e7b09f18SPeter Boström * Add iFunction or iInterface to names when available as additional 2135e7b09f18SPeter Boström * distinguishers between interfaces. iFunction is prioritized over 2136e7b09f18SPeter Boström * iInterface which matches Windows behavior at the point of writing. 2137e7b09f18SPeter Boström */ 2138e7b09f18SPeter Boström if (intf->intf_assoc && intf->intf_assoc->iFunction != 0) 2139e7b09f18SPeter Boström function = intf->intf_assoc->iFunction; 2140e7b09f18SPeter Boström else 2141e7b09f18SPeter Boström function = intf->cur_altsetting->desc.iInterface; 2142e7b09f18SPeter Boström if (function != 0) { 2143e7b09f18SPeter Boström size_t len; 2144e7b09f18SPeter Boström 2145e7b09f18SPeter Boström strlcat(dev->name, ": ", sizeof(dev->name)); 2146e7b09f18SPeter Boström len = strlen(dev->name); 2147e7b09f18SPeter Boström usb_string(udev, function, dev->name + len, 2148e7b09f18SPeter Boström sizeof(dev->name) - len); 2149e7b09f18SPeter Boström } 2150e7b09f18SPeter Boström 21510c0d06caSMauro Carvalho Chehab /* Parse the Video Class control descriptor. */ 21520c0d06caSMauro Carvalho Chehab if (uvc_parse_control(dev) < 0) { 21530c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC " 21540c0d06caSMauro Carvalho Chehab "descriptors.\n"); 21550c0d06caSMauro Carvalho Chehab goto error; 21560c0d06caSMauro Carvalho Chehab } 21570c0d06caSMauro Carvalho Chehab 21580c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n", 21590c0d06caSMauro Carvalho Chehab dev->uvc_version >> 8, dev->uvc_version & 0xff, 21600c0d06caSMauro Carvalho Chehab udev->product ? udev->product : "<unnamed>", 21610c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idVendor), 21620c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idProduct)); 21630c0d06caSMauro Carvalho Chehab 21643a03284dSLaurent Pinchart if (dev->quirks != dev->info->quirks) { 21650c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module " 21660c0d06caSMauro Carvalho Chehab "parameter for testing purpose.\n", dev->quirks); 21670c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "Please report required quirks to the " 21680c0d06caSMauro Carvalho Chehab "linux-uvc-devel mailing list.\n"); 21690c0d06caSMauro Carvalho Chehab } 21700c0d06caSMauro Carvalho Chehab 21719832e155SJavier Martinez Canillas /* Initialize the media device and register the V4L2 device. */ 21720c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 21730c0d06caSMauro Carvalho Chehab dev->mdev.dev = &intf->dev; 2174c0decac1SMauro Carvalho Chehab strscpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model)); 21750c0d06caSMauro Carvalho Chehab if (udev->serial) 2176c0decac1SMauro Carvalho Chehab strscpy(dev->mdev.serial, udev->serial, 21770c0d06caSMauro Carvalho Chehab sizeof(dev->mdev.serial)); 2178cc1e6315SMauro Carvalho Chehab strscpy(dev->mdev.bus_info, udev->devpath, sizeof(dev->mdev.bus_info)); 21790c0d06caSMauro Carvalho Chehab dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); 21809832e155SJavier Martinez Canillas media_device_init(&dev->mdev); 21810c0d06caSMauro Carvalho Chehab 21820c0d06caSMauro Carvalho Chehab dev->vdev.mdev = &dev->mdev; 21830c0d06caSMauro Carvalho Chehab #endif 21840c0d06caSMauro Carvalho Chehab if (v4l2_device_register(&intf->dev, &dev->vdev) < 0) 21850c0d06caSMauro Carvalho Chehab goto error; 21860c0d06caSMauro Carvalho Chehab 21870c0d06caSMauro Carvalho Chehab /* Initialize controls. */ 21880c0d06caSMauro Carvalho Chehab if (uvc_ctrl_init_device(dev) < 0) 21890c0d06caSMauro Carvalho Chehab goto error; 21900c0d06caSMauro Carvalho Chehab 21910c0d06caSMauro Carvalho Chehab /* Scan the device for video chains. */ 21920c0d06caSMauro Carvalho Chehab if (uvc_scan_device(dev) < 0) 21930c0d06caSMauro Carvalho Chehab goto error; 21940c0d06caSMauro Carvalho Chehab 21950c0d06caSMauro Carvalho Chehab /* Register video device nodes. */ 21960c0d06caSMauro Carvalho Chehab if (uvc_register_chains(dev) < 0) 21970c0d06caSMauro Carvalho Chehab goto error; 21980c0d06caSMauro Carvalho Chehab 21999832e155SJavier Martinez Canillas #ifdef CONFIG_MEDIA_CONTROLLER 22009832e155SJavier Martinez Canillas /* Register the media device node */ 22019832e155SJavier Martinez Canillas if (media_device_register(&dev->mdev) < 0) 22029832e155SJavier Martinez Canillas goto error; 22039832e155SJavier Martinez Canillas #endif 22040c0d06caSMauro Carvalho Chehab /* Save our data pointer in the interface data. */ 22050c0d06caSMauro Carvalho Chehab usb_set_intfdata(intf, dev); 22060c0d06caSMauro Carvalho Chehab 22070c0d06caSMauro Carvalho Chehab /* Initialize the interrupt URB. */ 22080c0d06caSMauro Carvalho Chehab if ((ret = uvc_status_init(dev)) < 0) { 22090c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "Unable to initialize the status " 22100c0d06caSMauro Carvalho Chehab "endpoint (%d), status interrupt will not be " 22110c0d06caSMauro Carvalho Chehab "supported.\n", ret); 22120c0d06caSMauro Carvalho Chehab } 22130c0d06caSMauro Carvalho Chehab 22140c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n"); 22150c0d06caSMauro Carvalho Chehab usb_enable_autosuspend(udev); 22160c0d06caSMauro Carvalho Chehab return 0; 22170c0d06caSMauro Carvalho Chehab 22180c0d06caSMauro Carvalho Chehab error: 22190c0d06caSMauro Carvalho Chehab uvc_unregister_video(dev); 2220f9ffcb0aSPhilipp Zabel kref_put(&dev->ref, uvc_delete); 22210c0d06caSMauro Carvalho Chehab return -ENODEV; 22220c0d06caSMauro Carvalho Chehab } 22230c0d06caSMauro Carvalho Chehab 22240c0d06caSMauro Carvalho Chehab static void uvc_disconnect(struct usb_interface *intf) 22250c0d06caSMauro Carvalho Chehab { 22260c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 22270c0d06caSMauro Carvalho Chehab 22280c0d06caSMauro Carvalho Chehab /* Set the USB interface data to NULL. This can be done outside the 22290c0d06caSMauro Carvalho Chehab * lock, as there's no other reader. 22300c0d06caSMauro Carvalho Chehab */ 22310c0d06caSMauro Carvalho Chehab usb_set_intfdata(intf, NULL); 22320c0d06caSMauro Carvalho Chehab 22330c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 22340c0d06caSMauro Carvalho Chehab UVC_SC_VIDEOSTREAMING) 22350c0d06caSMauro Carvalho Chehab return; 22360c0d06caSMauro Carvalho Chehab 22370c0d06caSMauro Carvalho Chehab uvc_unregister_video(dev); 2238f9ffcb0aSPhilipp Zabel kref_put(&dev->ref, uvc_delete); 22390c0d06caSMauro Carvalho Chehab } 22400c0d06caSMauro Carvalho Chehab 22410c0d06caSMauro Carvalho Chehab static int uvc_suspend(struct usb_interface *intf, pm_message_t message) 22420c0d06caSMauro Carvalho Chehab { 22430c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 22440c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 22450c0d06caSMauro Carvalho Chehab 22460c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n", 22470c0d06caSMauro Carvalho Chehab intf->cur_altsetting->desc.bInterfaceNumber); 22480c0d06caSMauro Carvalho Chehab 22490c0d06caSMauro Carvalho Chehab /* Controls are cached on the fly so they don't need to be saved. */ 22500c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 225117706f56SLaurent Pinchart UVC_SC_VIDEOCONTROL) { 225217706f56SLaurent Pinchart mutex_lock(&dev->lock); 225317706f56SLaurent Pinchart if (dev->users) 225417706f56SLaurent Pinchart uvc_status_stop(dev); 225517706f56SLaurent Pinchart mutex_unlock(&dev->lock); 225617706f56SLaurent Pinchart return 0; 225717706f56SLaurent Pinchart } 22580c0d06caSMauro Carvalho Chehab 22590c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 22600c0d06caSMauro Carvalho Chehab if (stream->intf == intf) 22610c0d06caSMauro Carvalho Chehab return uvc_video_suspend(stream); 22620c0d06caSMauro Carvalho Chehab } 22630c0d06caSMauro Carvalho Chehab 22640c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface " 22650c0d06caSMauro Carvalho Chehab "mismatch.\n"); 22660c0d06caSMauro Carvalho Chehab return -EINVAL; 22670c0d06caSMauro Carvalho Chehab } 22680c0d06caSMauro Carvalho Chehab 22690c0d06caSMauro Carvalho Chehab static int __uvc_resume(struct usb_interface *intf, int reset) 22700c0d06caSMauro Carvalho Chehab { 22710c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 22720c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 2273b83bba24SLaurent Pinchart int ret = 0; 22740c0d06caSMauro Carvalho Chehab 22750c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n", 22760c0d06caSMauro Carvalho Chehab intf->cur_altsetting->desc.bInterfaceNumber); 22770c0d06caSMauro Carvalho Chehab 22780c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 22790c0d06caSMauro Carvalho Chehab UVC_SC_VIDEOCONTROL) { 228017706f56SLaurent Pinchart if (reset) { 228117e1319fSWilliam Manley ret = uvc_ctrl_restore_values(dev); 22820c0d06caSMauro Carvalho Chehab if (ret < 0) 22830c0d06caSMauro Carvalho Chehab return ret; 22840c0d06caSMauro Carvalho Chehab } 22850c0d06caSMauro Carvalho Chehab 228617706f56SLaurent Pinchart mutex_lock(&dev->lock); 228717706f56SLaurent Pinchart if (dev->users) 228817706f56SLaurent Pinchart ret = uvc_status_start(dev, GFP_NOIO); 228917706f56SLaurent Pinchart mutex_unlock(&dev->lock); 229017706f56SLaurent Pinchart 229117706f56SLaurent Pinchart return ret; 22920c0d06caSMauro Carvalho Chehab } 22930c0d06caSMauro Carvalho Chehab 22940c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 2295b83bba24SLaurent Pinchart if (stream->intf == intf) { 2296b83bba24SLaurent Pinchart ret = uvc_video_resume(stream, reset); 2297b83bba24SLaurent Pinchart if (ret < 0) 22980da4ab98SLaurent Pinchart uvc_queue_streamoff(&stream->queue, 22990da4ab98SLaurent Pinchart stream->queue.queue.type); 2300b83bba24SLaurent Pinchart return ret; 2301b83bba24SLaurent Pinchart } 23020c0d06caSMauro Carvalho Chehab } 23030c0d06caSMauro Carvalho Chehab 23040c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface " 23050c0d06caSMauro Carvalho Chehab "mismatch.\n"); 23060c0d06caSMauro Carvalho Chehab return -EINVAL; 23070c0d06caSMauro Carvalho Chehab } 23080c0d06caSMauro Carvalho Chehab 23090c0d06caSMauro Carvalho Chehab static int uvc_resume(struct usb_interface *intf) 23100c0d06caSMauro Carvalho Chehab { 23110c0d06caSMauro Carvalho Chehab return __uvc_resume(intf, 0); 23120c0d06caSMauro Carvalho Chehab } 23130c0d06caSMauro Carvalho Chehab 23140c0d06caSMauro Carvalho Chehab static int uvc_reset_resume(struct usb_interface *intf) 23150c0d06caSMauro Carvalho Chehab { 23160c0d06caSMauro Carvalho Chehab return __uvc_resume(intf, 1); 23170c0d06caSMauro Carvalho Chehab } 23180c0d06caSMauro Carvalho Chehab 23190c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 23200c0d06caSMauro Carvalho Chehab * Module parameters 23210c0d06caSMauro Carvalho Chehab */ 23220c0d06caSMauro Carvalho Chehab 2323e4dca7b7SKees Cook static int uvc_clock_param_get(char *buffer, const struct kernel_param *kp) 23240c0d06caSMauro Carvalho Chehab { 23250c0d06caSMauro Carvalho Chehab if (uvc_clock_param == CLOCK_MONOTONIC) 23260c0d06caSMauro Carvalho Chehab return sprintf(buffer, "CLOCK_MONOTONIC"); 23270c0d06caSMauro Carvalho Chehab else 23280c0d06caSMauro Carvalho Chehab return sprintf(buffer, "CLOCK_REALTIME"); 23290c0d06caSMauro Carvalho Chehab } 23300c0d06caSMauro Carvalho Chehab 2331e4dca7b7SKees Cook static int uvc_clock_param_set(const char *val, const struct kernel_param *kp) 23320c0d06caSMauro Carvalho Chehab { 23330c0d06caSMauro Carvalho Chehab if (strncasecmp(val, "clock_", strlen("clock_")) == 0) 23340c0d06caSMauro Carvalho Chehab val += strlen("clock_"); 23350c0d06caSMauro Carvalho Chehab 23360c0d06caSMauro Carvalho Chehab if (strcasecmp(val, "monotonic") == 0) 23370c0d06caSMauro Carvalho Chehab uvc_clock_param = CLOCK_MONOTONIC; 23380c0d06caSMauro Carvalho Chehab else if (strcasecmp(val, "realtime") == 0) 23390c0d06caSMauro Carvalho Chehab uvc_clock_param = CLOCK_REALTIME; 23400c0d06caSMauro Carvalho Chehab else 23410c0d06caSMauro Carvalho Chehab return -EINVAL; 23420c0d06caSMauro Carvalho Chehab 23430c0d06caSMauro Carvalho Chehab return 0; 23440c0d06caSMauro Carvalho Chehab } 23450c0d06caSMauro Carvalho Chehab 23460c0d06caSMauro Carvalho Chehab module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get, 23470c0d06caSMauro Carvalho Chehab &uvc_clock_param, S_IRUGO|S_IWUSR); 23480c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(clock, "Video buffers timestamp clock"); 23495d0fd3c8SLaurent Pinchart module_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, S_IRUGO|S_IWUSR); 23505d0fd3c8SLaurent Pinchart MODULE_PARM_DESC(hwtimestamps, "Use hardware timestamps"); 23510c0d06caSMauro Carvalho Chehab module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR); 23520c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); 23530c0d06caSMauro Carvalho Chehab module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); 23540c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(quirks, "Forced device quirks"); 23550c0d06caSMauro Carvalho Chehab module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR); 23560c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(trace, "Trace level bitmask"); 23570c0d06caSMauro Carvalho Chehab module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR); 23580c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); 23590c0d06caSMauro Carvalho Chehab 23600c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 23610c0d06caSMauro Carvalho Chehab * Driver initialization and cleanup 23620c0d06caSMauro Carvalho Chehab */ 23630c0d06caSMauro Carvalho Chehab 23643bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_probe_minmax = { 23653bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_PROBE_MINMAX, 23663bc85817SGuennadi Liakhovetski }; 23673bc85817SGuennadi Liakhovetski 23683bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_fix_bandwidth = { 23693bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_FIX_BANDWIDTH, 23703bc85817SGuennadi Liakhovetski }; 23713bc85817SGuennadi Liakhovetski 23723bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_probe_def = { 23733bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_PROBE_DEF, 23743bc85817SGuennadi Liakhovetski }; 23753bc85817SGuennadi Liakhovetski 23763bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_stream_no_fid = { 23773bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_STREAM_NO_FID, 23783bc85817SGuennadi Liakhovetski }; 23793bc85817SGuennadi Liakhovetski 23803bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_force_y8 = { 23813bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_FORCE_Y8, 23823bc85817SGuennadi Liakhovetski }; 23833bc85817SGuennadi Liakhovetski 238488d8034cSGuennadi Liakhovetski #define UVC_INFO_QUIRK(q) (kernel_ulong_t)&(struct uvc_device_info){.quirks = q} 23856ea0d588SGuennadi Liakhovetski #define UVC_INFO_META(m) (kernel_ulong_t)&(struct uvc_device_info) \ 23866ea0d588SGuennadi Liakhovetski {.meta_format = m} 23873bc85817SGuennadi Liakhovetski 23880c0d06caSMauro Carvalho Chehab /* 23890c0d06caSMauro Carvalho Chehab * The Logitech cameras listed below have their interface class set to 23900c0d06caSMauro Carvalho Chehab * VENDOR_SPEC because they don't announce themselves as UVC devices, even 23910c0d06caSMauro Carvalho Chehab * though they are compliant. 23920c0d06caSMauro Carvalho Chehab */ 23937fb2e072SArvind Yadav static const struct usb_device_id uvc_ids[] = { 23940c0d06caSMauro Carvalho Chehab /* LogiLink Wireless Webcam */ 23950c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 23960c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 23970c0d06caSMauro Carvalho Chehab .idVendor = 0x0416, 23980c0d06caSMauro Carvalho Chehab .idProduct = 0xa91a, 23990c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 24000c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24010c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 24023bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 24030c0d06caSMauro Carvalho Chehab /* Genius eFace 2025 */ 24040c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24050c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24060c0d06caSMauro Carvalho Chehab .idVendor = 0x0458, 24070c0d06caSMauro Carvalho Chehab .idProduct = 0x706e, 24080c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 24090c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24100c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 24113bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 24120c0d06caSMauro Carvalho Chehab /* Microsoft Lifecam NX-6000 */ 24130c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24140c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24150c0d06caSMauro Carvalho Chehab .idVendor = 0x045e, 24160c0d06caSMauro Carvalho Chehab .idProduct = 0x00f8, 24170c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 24180c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24190c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 24203bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 24211558ec83SLaurent Pinchart /* Microsoft Lifecam NX-3000 */ 24221558ec83SLaurent Pinchart { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24231558ec83SLaurent Pinchart | USB_DEVICE_ID_MATCH_INT_INFO, 24241558ec83SLaurent Pinchart .idVendor = 0x045e, 24251558ec83SLaurent Pinchart .idProduct = 0x0721, 24261558ec83SLaurent Pinchart .bInterfaceClass = USB_CLASS_VIDEO, 24271558ec83SLaurent Pinchart .bInterfaceSubClass = 1, 24281558ec83SLaurent Pinchart .bInterfaceProtocol = 0, 24293bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 24300c0d06caSMauro Carvalho Chehab /* Microsoft Lifecam VX-7000 */ 24310c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24320c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24330c0d06caSMauro Carvalho Chehab .idVendor = 0x045e, 24340c0d06caSMauro Carvalho Chehab .idProduct = 0x0723, 24350c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 24360c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24370c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 24383bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 24390c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Fusion */ 24400c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24410c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24420c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 24430c0d06caSMauro Carvalho Chehab .idProduct = 0x08c1, 24440c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 24450c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24460c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 24470c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Orbit MP */ 24480c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24490c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24500c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 24510c0d06caSMauro Carvalho Chehab .idProduct = 0x08c2, 24520c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 24530c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24540c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 24550c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Pro for Notebook */ 24560c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24570c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24580c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 24590c0d06caSMauro Carvalho Chehab .idProduct = 0x08c3, 24600c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 24610c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24620c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 24630c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Pro 5000 */ 24640c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24650c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24660c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 24670c0d06caSMauro Carvalho Chehab .idProduct = 0x08c5, 24680c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 24690c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24700c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 24710c0d06caSMauro Carvalho Chehab /* Logitech Quickcam OEM Dell Notebook */ 24720c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24730c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24740c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 24750c0d06caSMauro Carvalho Chehab .idProduct = 0x08c6, 24760c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 24770c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24780c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 24790c0d06caSMauro Carvalho Chehab /* Logitech Quickcam OEM Cisco VT Camera II */ 24800c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24810c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24820c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 24830c0d06caSMauro Carvalho Chehab .idProduct = 0x08c7, 24840c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 24850c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24860c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 248717e1319fSWilliam Manley /* Logitech HD Pro Webcam C920 */ 248817e1319fSWilliam Manley { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 248917e1319fSWilliam Manley | USB_DEVICE_ID_MATCH_INT_INFO, 249017e1319fSWilliam Manley .idVendor = 0x046d, 249117e1319fSWilliam Manley .idProduct = 0x082d, 249217e1319fSWilliam Manley .bInterfaceClass = USB_CLASS_VIDEO, 249317e1319fSWilliam Manley .bInterfaceSubClass = 1, 249417e1319fSWilliam Manley .bInterfaceProtocol = 0, 249588d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT) }, 24960c0d06caSMauro Carvalho Chehab /* Chicony CNF7129 (Asus EEE 100HE) */ 24970c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24980c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24990c0d06caSMauro Carvalho Chehab .idVendor = 0x04f2, 25000c0d06caSMauro Carvalho Chehab .idProduct = 0xb071, 25010c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 25020c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 25030c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 250488d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTRICT_FRAME_RATE) }, 25050c0d06caSMauro Carvalho Chehab /* Alcor Micro AU3820 (Future Boy PC USB Webcam) */ 25060c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25070c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 25080c0d06caSMauro Carvalho Chehab .idVendor = 0x058f, 25090c0d06caSMauro Carvalho Chehab .idProduct = 0x3820, 25100c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 25110c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 25120c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 25133bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 25140c0d06caSMauro Carvalho Chehab /* Dell XPS m1530 */ 25150c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25160c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 25170c0d06caSMauro Carvalho Chehab .idVendor = 0x05a9, 25180c0d06caSMauro Carvalho Chehab .idProduct = 0x2640, 25190c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 25200c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 25210c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 25223bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 252389e0f248SJoseph Salisbury /* Dell SP2008WFP Monitor */ 252489e0f248SJoseph Salisbury { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 252589e0f248SJoseph Salisbury | USB_DEVICE_ID_MATCH_INT_INFO, 252689e0f248SJoseph Salisbury .idVendor = 0x05a9, 252789e0f248SJoseph Salisbury .idProduct = 0x2641, 252889e0f248SJoseph Salisbury .bInterfaceClass = USB_CLASS_VIDEO, 252989e0f248SJoseph Salisbury .bInterfaceSubClass = 1, 253089e0f248SJoseph Salisbury .bInterfaceProtocol = 0, 25313bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 2532c2a273b2SJoseph Salisbury /* Dell Alienware X51 */ 2533c2a273b2SJoseph Salisbury { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2534c2a273b2SJoseph Salisbury | USB_DEVICE_ID_MATCH_INT_INFO, 2535c2a273b2SJoseph Salisbury .idVendor = 0x05a9, 2536c2a273b2SJoseph Salisbury .idProduct = 0x2643, 2537c2a273b2SJoseph Salisbury .bInterfaceClass = USB_CLASS_VIDEO, 2538c2a273b2SJoseph Salisbury .bInterfaceSubClass = 1, 2539c2a273b2SJoseph Salisbury .bInterfaceProtocol = 0, 25403bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 2541afcf44c7SKamal Mostafa /* Dell Studio Hybrid 140g (OmniVision webcam) */ 2542afcf44c7SKamal Mostafa { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2543afcf44c7SKamal Mostafa | USB_DEVICE_ID_MATCH_INT_INFO, 2544afcf44c7SKamal Mostafa .idVendor = 0x05a9, 2545afcf44c7SKamal Mostafa .idProduct = 0x264a, 2546afcf44c7SKamal Mostafa .bInterfaceClass = USB_CLASS_VIDEO, 2547afcf44c7SKamal Mostafa .bInterfaceSubClass = 1, 2548afcf44c7SKamal Mostafa .bInterfaceProtocol = 0, 25493bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 255062ea864fSPaul Fertser /* Dell XPS M1330 (OmniVision OV7670 webcam) */ 255162ea864fSPaul Fertser { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 255262ea864fSPaul Fertser | USB_DEVICE_ID_MATCH_INT_INFO, 255362ea864fSPaul Fertser .idVendor = 0x05a9, 255462ea864fSPaul Fertser .idProduct = 0x7670, 255562ea864fSPaul Fertser .bInterfaceClass = USB_CLASS_VIDEO, 255662ea864fSPaul Fertser .bInterfaceSubClass = 1, 255762ea864fSPaul Fertser .bInterfaceProtocol = 0, 25583bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 25590c0d06caSMauro Carvalho Chehab /* Apple Built-In iSight */ 25600c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25610c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 25620c0d06caSMauro Carvalho Chehab .idVendor = 0x05ac, 25630c0d06caSMauro Carvalho Chehab .idProduct = 0x8501, 25640c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 25650c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 25660c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 256788d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 25683bc85817SGuennadi Liakhovetski | UVC_QUIRK_BUILTIN_ISIGHT) }, 25697b848ed6SDaniel Roschka /* Apple Built-In iSight via iBridge */ 25707b848ed6SDaniel Roschka { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25717b848ed6SDaniel Roschka | USB_DEVICE_ID_MATCH_INT_INFO, 25727b848ed6SDaniel Roschka .idVendor = 0x05ac, 25737b848ed6SDaniel Roschka .idProduct = 0x8600, 25747b848ed6SDaniel Roschka .bInterfaceClass = USB_CLASS_VIDEO, 25757b848ed6SDaniel Roschka .bInterfaceSubClass = 1, 25767b848ed6SDaniel Roschka .bInterfaceProtocol = 0, 25773bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 25780c0d06caSMauro Carvalho Chehab /* Foxlink ("HP Webcam" on HP Mini 5103) */ 25790c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25800c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 25810c0d06caSMauro Carvalho Chehab .idVendor = 0x05c8, 25820c0d06caSMauro Carvalho Chehab .idProduct = 0x0403, 25830c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 25840c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 25850c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 25863bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 25870c0d06caSMauro Carvalho Chehab /* Genesys Logic USB 2.0 PC Camera */ 25880c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25890c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 25900c0d06caSMauro Carvalho Chehab .idVendor = 0x05e3, 25910c0d06caSMauro Carvalho Chehab .idProduct = 0x0505, 25920c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 25930c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 25940c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 25953bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 25960c0d06caSMauro Carvalho Chehab /* Hercules Classic Silver */ 25970c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25980c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 25990c0d06caSMauro Carvalho Chehab .idVendor = 0x06f8, 26000c0d06caSMauro Carvalho Chehab .idProduct = 0x300c, 26010c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26020c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26030c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 26043bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 26050c0d06caSMauro Carvalho Chehab /* ViMicro Vega */ 26060c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26070c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26080c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 26090c0d06caSMauro Carvalho Chehab .idProduct = 0x332d, 26100c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26110c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26120c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 26133bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 26140c0d06caSMauro Carvalho Chehab /* ViMicro - Minoru3D */ 26150c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26160c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26170c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 26180c0d06caSMauro Carvalho Chehab .idProduct = 0x3410, 26190c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26200c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26210c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 26223bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 26230c0d06caSMauro Carvalho Chehab /* ViMicro Venus - Minoru3D */ 26240c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26250c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26260c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 26270c0d06caSMauro Carvalho Chehab .idProduct = 0x3420, 26280c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26290c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26300c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 26313bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 26320c0d06caSMauro Carvalho Chehab /* Ophir Optronics - SPCAM 620U */ 26330c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26340c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26350c0d06caSMauro Carvalho Chehab .idVendor = 0x0bd3, 26360c0d06caSMauro Carvalho Chehab .idProduct = 0x0555, 26370c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26380c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26390c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 26403bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 26410c0d06caSMauro Carvalho Chehab /* MT6227 */ 26420c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26430c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26440c0d06caSMauro Carvalho Chehab .idVendor = 0x0e8d, 26450c0d06caSMauro Carvalho Chehab .idProduct = 0x0004, 26460c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26470c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26480c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 264988d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 26503bc85817SGuennadi Liakhovetski | UVC_QUIRK_PROBE_DEF) }, 26510c0d06caSMauro Carvalho Chehab /* IMC Networks (Medion Akoya) */ 26520c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26530c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26540c0d06caSMauro Carvalho Chehab .idVendor = 0x13d3, 26550c0d06caSMauro Carvalho Chehab .idProduct = 0x5103, 26560c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26570c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26580c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 26593bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 26600c0d06caSMauro Carvalho Chehab /* JMicron USB2.0 XGA WebCam */ 26610c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26620c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26630c0d06caSMauro Carvalho Chehab .idVendor = 0x152d, 26640c0d06caSMauro Carvalho Chehab .idProduct = 0x0310, 26650c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26660c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26670c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 26683bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 26690c0d06caSMauro Carvalho Chehab /* Syntek (HP Spartan) */ 26700c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26710c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26720c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 26730c0d06caSMauro Carvalho Chehab .idProduct = 0x5212, 26740c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26750c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26760c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 26773bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 26780c0d06caSMauro Carvalho Chehab /* Syntek (Samsung Q310) */ 26790c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26800c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26810c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 26820c0d06caSMauro Carvalho Chehab .idProduct = 0x5931, 26830c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26840c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26850c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 26863bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 26870c0d06caSMauro Carvalho Chehab /* Syntek (Packard Bell EasyNote MX52 */ 26880c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26890c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26900c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 26910c0d06caSMauro Carvalho Chehab .idProduct = 0x8a12, 26920c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26930c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26940c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 26953bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 26960c0d06caSMauro Carvalho Chehab /* Syntek (Asus F9SG) */ 26970c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26980c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26990c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 27000c0d06caSMauro Carvalho Chehab .idProduct = 0x8a31, 27010c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27020c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27030c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27043bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 27050c0d06caSMauro Carvalho Chehab /* Syntek (Asus U3S) */ 27060c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27070c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27080c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 27090c0d06caSMauro Carvalho Chehab .idProduct = 0x8a33, 27100c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27110c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27120c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27133bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 27140c0d06caSMauro Carvalho Chehab /* Syntek (JAOtech Smart Terminal) */ 27150c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27160c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27170c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 27180c0d06caSMauro Carvalho Chehab .idProduct = 0x8a34, 27190c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27200c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27210c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27223bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 27230c0d06caSMauro Carvalho Chehab /* Miricle 307K */ 27240c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27250c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27260c0d06caSMauro Carvalho Chehab .idVendor = 0x17dc, 27270c0d06caSMauro Carvalho Chehab .idProduct = 0x0202, 27280c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27290c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27300c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27313bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 27320c0d06caSMauro Carvalho Chehab /* Lenovo Thinkpad SL400/SL500 */ 27330c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27340c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27350c0d06caSMauro Carvalho Chehab .idVendor = 0x17ef, 27360c0d06caSMauro Carvalho Chehab .idProduct = 0x480b, 27370c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27380c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27390c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27403bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 27410c0d06caSMauro Carvalho Chehab /* Aveo Technology USB 2.0 Camera */ 27420c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27430c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27440c0d06caSMauro Carvalho Chehab .idVendor = 0x1871, 27450c0d06caSMauro Carvalho Chehab .idProduct = 0x0306, 27460c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27470c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27480c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 274988d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 27503bc85817SGuennadi Liakhovetski | UVC_QUIRK_PROBE_EXTRAFIELDS) }, 2751fe652471SLaurent Pinchart /* Aveo Technology USB 2.0 Camera (Tasco USB Microscope) */ 2752fe652471SLaurent Pinchart { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2753fe652471SLaurent Pinchart | USB_DEVICE_ID_MATCH_INT_INFO, 2754fe652471SLaurent Pinchart .idVendor = 0x1871, 2755fe652471SLaurent Pinchart .idProduct = 0x0516, 2756fe652471SLaurent Pinchart .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 2757fe652471SLaurent Pinchart .bInterfaceSubClass = 1, 2758fe652471SLaurent Pinchart .bInterfaceProtocol = 0 }, 27590c0d06caSMauro Carvalho Chehab /* Ecamm Pico iMage */ 27600c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27610c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27620c0d06caSMauro Carvalho Chehab .idVendor = 0x18cd, 27630c0d06caSMauro Carvalho Chehab .idProduct = 0xcafe, 27640c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27650c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27660c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 276788d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_EXTRAFIELDS) }, 27680c0d06caSMauro Carvalho Chehab /* Manta MM-353 Plako */ 27690c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27700c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27710c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 27720c0d06caSMauro Carvalho Chehab .idProduct = 0x3188, 27730c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27740c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27750c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27763bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 27770c0d06caSMauro Carvalho Chehab /* FSC WebCam V30S */ 27780c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27790c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27800c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 27810c0d06caSMauro Carvalho Chehab .idProduct = 0x3288, 27820c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27830c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27840c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27853bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 27860c0d06caSMauro Carvalho Chehab /* Arkmicro unbranded */ 27870c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27880c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27890c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 27900c0d06caSMauro Carvalho Chehab .idProduct = 0x3290, 27910c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27920c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27930c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27943bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 27950c0d06caSMauro Carvalho Chehab /* The Imaging Source USB CCD cameras */ 27960c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27970c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27980c0d06caSMauro Carvalho Chehab .idVendor = 0x199e, 27990c0d06caSMauro Carvalho Chehab .idProduct = 0x8102, 28000c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 28010c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28020c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 28030c0d06caSMauro Carvalho Chehab /* Bodelin ProScopeHR */ 28040c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28050c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_DEV_HI 28060c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28070c0d06caSMauro Carvalho Chehab .idVendor = 0x19ab, 28080c0d06caSMauro Carvalho Chehab .idProduct = 0x1000, 28090c0d06caSMauro Carvalho Chehab .bcdDevice_hi = 0x0126, 28100c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28110c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28120c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 281388d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_STATUS_INTERVAL) }, 28140c0d06caSMauro Carvalho Chehab /* MSI StarCam 370i */ 28150c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28160c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28170c0d06caSMauro Carvalho Chehab .idVendor = 0x1b3b, 28180c0d06caSMauro Carvalho Chehab .idProduct = 0x2951, 28190c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28200c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28210c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 28223bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 2823589266bdSNeil Armstrong /* Generalplus Technology Inc. 808 Camera */ 2824589266bdSNeil Armstrong { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2825589266bdSNeil Armstrong | USB_DEVICE_ID_MATCH_INT_INFO, 2826589266bdSNeil Armstrong .idVendor = 0x1b3f, 2827589266bdSNeil Armstrong .idProduct = 0x2002, 2828589266bdSNeil Armstrong .bInterfaceClass = USB_CLASS_VIDEO, 2829589266bdSNeil Armstrong .bInterfaceSubClass = 1, 2830589266bdSNeil Armstrong .bInterfaceProtocol = 0, 2831589266bdSNeil Armstrong .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 28320c0d06caSMauro Carvalho Chehab /* SiGma Micro USB Web Camera */ 28330c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28340c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28350c0d06caSMauro Carvalho Chehab .idVendor = 0x1c4f, 28360c0d06caSMauro Carvalho Chehab .idProduct = 0x3000, 28370c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28380c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28390c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 284088d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 28413bc85817SGuennadi Liakhovetski | UVC_QUIRK_IGNORE_SELECTOR_UNIT) }, 2842e1b78a33SPhilipp Zabel /* Oculus VR Positional Tracker DK2 */ 2843e1b78a33SPhilipp Zabel { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2844e1b78a33SPhilipp Zabel | USB_DEVICE_ID_MATCH_INT_INFO, 2845e1b78a33SPhilipp Zabel .idVendor = 0x2833, 2846e1b78a33SPhilipp Zabel .idProduct = 0x0201, 2847e1b78a33SPhilipp Zabel .bInterfaceClass = USB_CLASS_VIDEO, 2848e1b78a33SPhilipp Zabel .bInterfaceSubClass = 1, 2849e1b78a33SPhilipp Zabel .bInterfaceProtocol = 0, 28503bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_force_y8 }, 285103c47aaeSPhilipp Zabel /* Oculus VR Rift Sensor */ 285203c47aaeSPhilipp Zabel { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 285303c47aaeSPhilipp Zabel | USB_DEVICE_ID_MATCH_INT_INFO, 285403c47aaeSPhilipp Zabel .idVendor = 0x2833, 285503c47aaeSPhilipp Zabel .idProduct = 0x0211, 285603c47aaeSPhilipp Zabel .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 285703c47aaeSPhilipp Zabel .bInterfaceSubClass = 1, 285803c47aaeSPhilipp Zabel .bInterfaceProtocol = 0, 28593bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_force_y8 }, 28606ea0d588SGuennadi Liakhovetski /* Intel RealSense D4M */ 28616ea0d588SGuennadi Liakhovetski { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28626ea0d588SGuennadi Liakhovetski | USB_DEVICE_ID_MATCH_INT_INFO, 28636ea0d588SGuennadi Liakhovetski .idVendor = 0x8086, 28646ea0d588SGuennadi Liakhovetski .idProduct = 0x0b03, 28656ea0d588SGuennadi Liakhovetski .bInterfaceClass = USB_CLASS_VIDEO, 28666ea0d588SGuennadi Liakhovetski .bInterfaceSubClass = 1, 28676ea0d588SGuennadi Liakhovetski .bInterfaceProtocol = 0, 28686ea0d588SGuennadi Liakhovetski .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 28690c0d06caSMauro Carvalho Chehab /* Generic USB Video Class */ 28708afe97beSLaurent Pinchart { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_UNDEFINED) }, 28718afe97beSLaurent Pinchart { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_15) }, 28720c0d06caSMauro Carvalho Chehab {} 28730c0d06caSMauro Carvalho Chehab }; 28740c0d06caSMauro Carvalho Chehab 28750c0d06caSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, uvc_ids); 28760c0d06caSMauro Carvalho Chehab 28770c0d06caSMauro Carvalho Chehab struct uvc_driver uvc_driver = { 28780c0d06caSMauro Carvalho Chehab .driver = { 28790c0d06caSMauro Carvalho Chehab .name = "uvcvideo", 28800c0d06caSMauro Carvalho Chehab .probe = uvc_probe, 28810c0d06caSMauro Carvalho Chehab .disconnect = uvc_disconnect, 28820c0d06caSMauro Carvalho Chehab .suspend = uvc_suspend, 28830c0d06caSMauro Carvalho Chehab .resume = uvc_resume, 28840c0d06caSMauro Carvalho Chehab .reset_resume = uvc_reset_resume, 28850c0d06caSMauro Carvalho Chehab .id_table = uvc_ids, 28860c0d06caSMauro Carvalho Chehab .supports_autosuspend = 1, 28870c0d06caSMauro Carvalho Chehab }, 28880c0d06caSMauro Carvalho Chehab }; 28890c0d06caSMauro Carvalho Chehab 28900c0d06caSMauro Carvalho Chehab static int __init uvc_init(void) 28910c0d06caSMauro Carvalho Chehab { 28920c0d06caSMauro Carvalho Chehab int ret; 28930c0d06caSMauro Carvalho Chehab 28940c0d06caSMauro Carvalho Chehab uvc_debugfs_init(); 28950c0d06caSMauro Carvalho Chehab 28960c0d06caSMauro Carvalho Chehab ret = usb_register(&uvc_driver.driver); 28970c0d06caSMauro Carvalho Chehab if (ret < 0) { 28980c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup(); 28990c0d06caSMauro Carvalho Chehab return ret; 29000c0d06caSMauro Carvalho Chehab } 29010c0d06caSMauro Carvalho Chehab 29020c0d06caSMauro Carvalho Chehab printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n"); 29030c0d06caSMauro Carvalho Chehab return 0; 29040c0d06caSMauro Carvalho Chehab } 29050c0d06caSMauro Carvalho Chehab 29060c0d06caSMauro Carvalho Chehab static void __exit uvc_cleanup(void) 29070c0d06caSMauro Carvalho Chehab { 29080c0d06caSMauro Carvalho Chehab usb_deregister(&uvc_driver.driver); 29090c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup(); 29100c0d06caSMauro Carvalho Chehab } 29110c0d06caSMauro Carvalho Chehab 29120c0d06caSMauro Carvalho Chehab module_init(uvc_init); 29130c0d06caSMauro Carvalho Chehab module_exit(uvc_cleanup); 29140c0d06caSMauro Carvalho Chehab 29150c0d06caSMauro Carvalho Chehab MODULE_AUTHOR(DRIVER_AUTHOR); 29160c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION(DRIVER_DESC); 29170c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 29180c0d06caSMauro Carvalho Chehab MODULE_VERSION(DRIVER_VERSION); 29190c0d06caSMauro Carvalho Chehab 2920