1*0c0d06caSMauro Carvalho Chehab /* 2*0c0d06caSMauro Carvalho Chehab * uvc_driver.c -- USB Video Class driver 3*0c0d06caSMauro Carvalho Chehab * 4*0c0d06caSMauro Carvalho Chehab * Copyright (C) 2005-2010 5*0c0d06caSMauro Carvalho Chehab * Laurent Pinchart (laurent.pinchart@ideasonboard.com) 6*0c0d06caSMauro Carvalho Chehab * 7*0c0d06caSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 8*0c0d06caSMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 9*0c0d06caSMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 10*0c0d06caSMauro Carvalho Chehab * (at your option) any later version. 11*0c0d06caSMauro Carvalho Chehab * 12*0c0d06caSMauro Carvalho Chehab */ 13*0c0d06caSMauro Carvalho Chehab 14*0c0d06caSMauro Carvalho Chehab /* 15*0c0d06caSMauro Carvalho Chehab * This driver aims to support video input and ouput devices compliant with the 16*0c0d06caSMauro Carvalho Chehab * 'USB Video Class' specification. 17*0c0d06caSMauro Carvalho Chehab * 18*0c0d06caSMauro Carvalho Chehab * The driver doesn't support the deprecated v4l1 interface. It implements the 19*0c0d06caSMauro Carvalho Chehab * mmap capture method only, and doesn't do any image format conversion in 20*0c0d06caSMauro Carvalho Chehab * software. If your user-space application doesn't support YUYV or MJPEG, fix 21*0c0d06caSMauro Carvalho Chehab * it :-). Please note that the MJPEG data have been stripped from their 22*0c0d06caSMauro Carvalho Chehab * Huffman tables (DHT marker), you will need to add it back if your JPEG 23*0c0d06caSMauro Carvalho Chehab * codec can't handle MJPEG data. 24*0c0d06caSMauro Carvalho Chehab */ 25*0c0d06caSMauro Carvalho Chehab 26*0c0d06caSMauro Carvalho Chehab #include <linux/atomic.h> 27*0c0d06caSMauro Carvalho Chehab #include <linux/kernel.h> 28*0c0d06caSMauro Carvalho Chehab #include <linux/list.h> 29*0c0d06caSMauro Carvalho Chehab #include <linux/module.h> 30*0c0d06caSMauro Carvalho Chehab #include <linux/slab.h> 31*0c0d06caSMauro Carvalho Chehab #include <linux/usb.h> 32*0c0d06caSMauro Carvalho Chehab #include <linux/videodev2.h> 33*0c0d06caSMauro Carvalho Chehab #include <linux/vmalloc.h> 34*0c0d06caSMauro Carvalho Chehab #include <linux/wait.h> 35*0c0d06caSMauro Carvalho Chehab #include <linux/version.h> 36*0c0d06caSMauro Carvalho Chehab #include <asm/unaligned.h> 37*0c0d06caSMauro Carvalho Chehab 38*0c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h> 39*0c0d06caSMauro Carvalho Chehab 40*0c0d06caSMauro Carvalho Chehab #include "uvcvideo.h" 41*0c0d06caSMauro Carvalho Chehab 42*0c0d06caSMauro Carvalho Chehab #define DRIVER_AUTHOR "Laurent Pinchart " \ 43*0c0d06caSMauro Carvalho Chehab "<laurent.pinchart@ideasonboard.com>" 44*0c0d06caSMauro Carvalho Chehab #define DRIVER_DESC "USB Video Class driver" 45*0c0d06caSMauro Carvalho Chehab 46*0c0d06caSMauro Carvalho Chehab unsigned int uvc_clock_param = CLOCK_MONOTONIC; 47*0c0d06caSMauro Carvalho Chehab unsigned int uvc_no_drop_param; 48*0c0d06caSMauro Carvalho Chehab static unsigned int uvc_quirks_param = -1; 49*0c0d06caSMauro Carvalho Chehab unsigned int uvc_trace_param; 50*0c0d06caSMauro Carvalho Chehab unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; 51*0c0d06caSMauro Carvalho Chehab 52*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 53*0c0d06caSMauro Carvalho Chehab * Video formats 54*0c0d06caSMauro Carvalho Chehab */ 55*0c0d06caSMauro Carvalho Chehab 56*0c0d06caSMauro Carvalho Chehab static struct uvc_format_desc uvc_fmts[] = { 57*0c0d06caSMauro Carvalho Chehab { 58*0c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:2 (YUYV)", 59*0c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_YUY2, 60*0c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_YUYV, 61*0c0d06caSMauro Carvalho Chehab }, 62*0c0d06caSMauro Carvalho Chehab { 63*0c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:2 (YUYV)", 64*0c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_YUY2_ISIGHT, 65*0c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_YUYV, 66*0c0d06caSMauro Carvalho Chehab }, 67*0c0d06caSMauro Carvalho Chehab { 68*0c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:0 (NV12)", 69*0c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_NV12, 70*0c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_NV12, 71*0c0d06caSMauro Carvalho Chehab }, 72*0c0d06caSMauro Carvalho Chehab { 73*0c0d06caSMauro Carvalho Chehab .name = "MJPEG", 74*0c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_MJPEG, 75*0c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_MJPEG, 76*0c0d06caSMauro Carvalho Chehab }, 77*0c0d06caSMauro Carvalho Chehab { 78*0c0d06caSMauro Carvalho Chehab .name = "YVU 4:2:0 (YV12)", 79*0c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_YV12, 80*0c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_YVU420, 81*0c0d06caSMauro Carvalho Chehab }, 82*0c0d06caSMauro Carvalho Chehab { 83*0c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:0 (I420)", 84*0c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_I420, 85*0c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_YUV420, 86*0c0d06caSMauro Carvalho Chehab }, 87*0c0d06caSMauro Carvalho Chehab { 88*0c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:0 (M420)", 89*0c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_M420, 90*0c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_M420, 91*0c0d06caSMauro Carvalho Chehab }, 92*0c0d06caSMauro Carvalho Chehab { 93*0c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:2 (UYVY)", 94*0c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_UYVY, 95*0c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_UYVY, 96*0c0d06caSMauro Carvalho Chehab }, 97*0c0d06caSMauro Carvalho Chehab { 98*0c0d06caSMauro Carvalho Chehab .name = "Greyscale 8-bit (Y800)", 99*0c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y800, 100*0c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_GREY, 101*0c0d06caSMauro Carvalho Chehab }, 102*0c0d06caSMauro Carvalho Chehab { 103*0c0d06caSMauro Carvalho Chehab .name = "Greyscale 8-bit (Y8 )", 104*0c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y8, 105*0c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_GREY, 106*0c0d06caSMauro Carvalho Chehab }, 107*0c0d06caSMauro Carvalho Chehab { 108*0c0d06caSMauro Carvalho Chehab .name = "Greyscale 10-bit (Y10 )", 109*0c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y10, 110*0c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_Y10, 111*0c0d06caSMauro Carvalho Chehab }, 112*0c0d06caSMauro Carvalho Chehab { 113*0c0d06caSMauro Carvalho Chehab .name = "Greyscale 12-bit (Y12 )", 114*0c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y12, 115*0c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_Y12, 116*0c0d06caSMauro Carvalho Chehab }, 117*0c0d06caSMauro Carvalho Chehab { 118*0c0d06caSMauro Carvalho Chehab .name = "Greyscale 16-bit (Y16 )", 119*0c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y16, 120*0c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_Y16, 121*0c0d06caSMauro Carvalho Chehab }, 122*0c0d06caSMauro Carvalho Chehab { 123*0c0d06caSMauro Carvalho Chehab .name = "RGB Bayer", 124*0c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_BY8, 125*0c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_SBGGR8, 126*0c0d06caSMauro Carvalho Chehab }, 127*0c0d06caSMauro Carvalho Chehab { 128*0c0d06caSMauro Carvalho Chehab .name = "RGB565", 129*0c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_RGBP, 130*0c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_RGB565, 131*0c0d06caSMauro Carvalho Chehab }, 132*0c0d06caSMauro Carvalho Chehab { 133*0c0d06caSMauro Carvalho Chehab .name = "H.264", 134*0c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_H264, 135*0c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_H264, 136*0c0d06caSMauro Carvalho Chehab }, 137*0c0d06caSMauro Carvalho Chehab }; 138*0c0d06caSMauro Carvalho Chehab 139*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 140*0c0d06caSMauro Carvalho Chehab * Utility functions 141*0c0d06caSMauro Carvalho Chehab */ 142*0c0d06caSMauro Carvalho Chehab 143*0c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, 144*0c0d06caSMauro Carvalho Chehab __u8 epaddr) 145*0c0d06caSMauro Carvalho Chehab { 146*0c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep; 147*0c0d06caSMauro Carvalho Chehab unsigned int i; 148*0c0d06caSMauro Carvalho Chehab 149*0c0d06caSMauro Carvalho Chehab for (i = 0; i < alts->desc.bNumEndpoints; ++i) { 150*0c0d06caSMauro Carvalho Chehab ep = &alts->endpoint[i]; 151*0c0d06caSMauro Carvalho Chehab if (ep->desc.bEndpointAddress == epaddr) 152*0c0d06caSMauro Carvalho Chehab return ep; 153*0c0d06caSMauro Carvalho Chehab } 154*0c0d06caSMauro Carvalho Chehab 155*0c0d06caSMauro Carvalho Chehab return NULL; 156*0c0d06caSMauro Carvalho Chehab } 157*0c0d06caSMauro Carvalho Chehab 158*0c0d06caSMauro Carvalho Chehab static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16]) 159*0c0d06caSMauro Carvalho Chehab { 160*0c0d06caSMauro Carvalho Chehab unsigned int len = ARRAY_SIZE(uvc_fmts); 161*0c0d06caSMauro Carvalho Chehab unsigned int i; 162*0c0d06caSMauro Carvalho Chehab 163*0c0d06caSMauro Carvalho Chehab for (i = 0; i < len; ++i) { 164*0c0d06caSMauro Carvalho Chehab if (memcmp(guid, uvc_fmts[i].guid, 16) == 0) 165*0c0d06caSMauro Carvalho Chehab return &uvc_fmts[i]; 166*0c0d06caSMauro Carvalho Chehab } 167*0c0d06caSMauro Carvalho Chehab 168*0c0d06caSMauro Carvalho Chehab return NULL; 169*0c0d06caSMauro Carvalho Chehab } 170*0c0d06caSMauro Carvalho Chehab 171*0c0d06caSMauro Carvalho Chehab static __u32 uvc_colorspace(const __u8 primaries) 172*0c0d06caSMauro Carvalho Chehab { 173*0c0d06caSMauro Carvalho Chehab static const __u8 colorprimaries[] = { 174*0c0d06caSMauro Carvalho Chehab 0, 175*0c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SRGB, 176*0c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_470_SYSTEM_M, 177*0c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_470_SYSTEM_BG, 178*0c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SMPTE170M, 179*0c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SMPTE240M, 180*0c0d06caSMauro Carvalho Chehab }; 181*0c0d06caSMauro Carvalho Chehab 182*0c0d06caSMauro Carvalho Chehab if (primaries < ARRAY_SIZE(colorprimaries)) 183*0c0d06caSMauro Carvalho Chehab return colorprimaries[primaries]; 184*0c0d06caSMauro Carvalho Chehab 185*0c0d06caSMauro Carvalho Chehab return 0; 186*0c0d06caSMauro Carvalho Chehab } 187*0c0d06caSMauro Carvalho Chehab 188*0c0d06caSMauro Carvalho Chehab /* Simplify a fraction using a simple continued fraction decomposition. The 189*0c0d06caSMauro Carvalho Chehab * idea here is to convert fractions such as 333333/10000000 to 1/30 using 190*0c0d06caSMauro Carvalho Chehab * 32 bit arithmetic only. The algorithm is not perfect and relies upon two 191*0c0d06caSMauro Carvalho Chehab * arbitrary parameters to remove non-significative terms from the simple 192*0c0d06caSMauro Carvalho Chehab * continued fraction decomposition. Using 8 and 333 for n_terms and threshold 193*0c0d06caSMauro Carvalho Chehab * respectively seems to give nice results. 194*0c0d06caSMauro Carvalho Chehab */ 195*0c0d06caSMauro Carvalho Chehab void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, 196*0c0d06caSMauro Carvalho Chehab unsigned int n_terms, unsigned int threshold) 197*0c0d06caSMauro Carvalho Chehab { 198*0c0d06caSMauro Carvalho Chehab uint32_t *an; 199*0c0d06caSMauro Carvalho Chehab uint32_t x, y, r; 200*0c0d06caSMauro Carvalho Chehab unsigned int i, n; 201*0c0d06caSMauro Carvalho Chehab 202*0c0d06caSMauro Carvalho Chehab an = kmalloc(n_terms * sizeof *an, GFP_KERNEL); 203*0c0d06caSMauro Carvalho Chehab if (an == NULL) 204*0c0d06caSMauro Carvalho Chehab return; 205*0c0d06caSMauro Carvalho Chehab 206*0c0d06caSMauro Carvalho Chehab /* Convert the fraction to a simple continued fraction. See 207*0c0d06caSMauro Carvalho Chehab * http://mathforum.org/dr.math/faq/faq.fractions.html 208*0c0d06caSMauro Carvalho Chehab * Stop if the current term is bigger than or equal to the given 209*0c0d06caSMauro Carvalho Chehab * threshold. 210*0c0d06caSMauro Carvalho Chehab */ 211*0c0d06caSMauro Carvalho Chehab x = *numerator; 212*0c0d06caSMauro Carvalho Chehab y = *denominator; 213*0c0d06caSMauro Carvalho Chehab 214*0c0d06caSMauro Carvalho Chehab for (n = 0; n < n_terms && y != 0; ++n) { 215*0c0d06caSMauro Carvalho Chehab an[n] = x / y; 216*0c0d06caSMauro Carvalho Chehab if (an[n] >= threshold) { 217*0c0d06caSMauro Carvalho Chehab if (n < 2) 218*0c0d06caSMauro Carvalho Chehab n++; 219*0c0d06caSMauro Carvalho Chehab break; 220*0c0d06caSMauro Carvalho Chehab } 221*0c0d06caSMauro Carvalho Chehab 222*0c0d06caSMauro Carvalho Chehab r = x - an[n] * y; 223*0c0d06caSMauro Carvalho Chehab x = y; 224*0c0d06caSMauro Carvalho Chehab y = r; 225*0c0d06caSMauro Carvalho Chehab } 226*0c0d06caSMauro Carvalho Chehab 227*0c0d06caSMauro Carvalho Chehab /* Expand the simple continued fraction back to an integer fraction. */ 228*0c0d06caSMauro Carvalho Chehab x = 0; 229*0c0d06caSMauro Carvalho Chehab y = 1; 230*0c0d06caSMauro Carvalho Chehab 231*0c0d06caSMauro Carvalho Chehab for (i = n; i > 0; --i) { 232*0c0d06caSMauro Carvalho Chehab r = y; 233*0c0d06caSMauro Carvalho Chehab y = an[i-1] * y + x; 234*0c0d06caSMauro Carvalho Chehab x = r; 235*0c0d06caSMauro Carvalho Chehab } 236*0c0d06caSMauro Carvalho Chehab 237*0c0d06caSMauro Carvalho Chehab *numerator = y; 238*0c0d06caSMauro Carvalho Chehab *denominator = x; 239*0c0d06caSMauro Carvalho Chehab kfree(an); 240*0c0d06caSMauro Carvalho Chehab } 241*0c0d06caSMauro Carvalho Chehab 242*0c0d06caSMauro Carvalho Chehab /* Convert a fraction to a frame interval in 100ns multiples. The idea here is 243*0c0d06caSMauro Carvalho Chehab * to compute numerator / denominator * 10000000 using 32 bit fixed point 244*0c0d06caSMauro Carvalho Chehab * arithmetic only. 245*0c0d06caSMauro Carvalho Chehab */ 246*0c0d06caSMauro Carvalho Chehab uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator) 247*0c0d06caSMauro Carvalho Chehab { 248*0c0d06caSMauro Carvalho Chehab uint32_t multiplier; 249*0c0d06caSMauro Carvalho Chehab 250*0c0d06caSMauro Carvalho Chehab /* Saturate the result if the operation would overflow. */ 251*0c0d06caSMauro Carvalho Chehab if (denominator == 0 || 252*0c0d06caSMauro Carvalho Chehab numerator/denominator >= ((uint32_t)-1)/10000000) 253*0c0d06caSMauro Carvalho Chehab return (uint32_t)-1; 254*0c0d06caSMauro Carvalho Chehab 255*0c0d06caSMauro Carvalho Chehab /* Divide both the denominator and the multiplier by two until 256*0c0d06caSMauro Carvalho Chehab * numerator * multiplier doesn't overflow. If anyone knows a better 257*0c0d06caSMauro Carvalho Chehab * algorithm please let me know. 258*0c0d06caSMauro Carvalho Chehab */ 259*0c0d06caSMauro Carvalho Chehab multiplier = 10000000; 260*0c0d06caSMauro Carvalho Chehab while (numerator > ((uint32_t)-1)/multiplier) { 261*0c0d06caSMauro Carvalho Chehab multiplier /= 2; 262*0c0d06caSMauro Carvalho Chehab denominator /= 2; 263*0c0d06caSMauro Carvalho Chehab } 264*0c0d06caSMauro Carvalho Chehab 265*0c0d06caSMauro Carvalho Chehab return denominator ? numerator * multiplier / denominator : 0; 266*0c0d06caSMauro Carvalho Chehab } 267*0c0d06caSMauro Carvalho Chehab 268*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 269*0c0d06caSMauro Carvalho Chehab * Terminal and unit management 270*0c0d06caSMauro Carvalho Chehab */ 271*0c0d06caSMauro Carvalho Chehab 272*0c0d06caSMauro Carvalho Chehab struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id) 273*0c0d06caSMauro Carvalho Chehab { 274*0c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 275*0c0d06caSMauro Carvalho Chehab 276*0c0d06caSMauro Carvalho Chehab list_for_each_entry(entity, &dev->entities, list) { 277*0c0d06caSMauro Carvalho Chehab if (entity->id == id) 278*0c0d06caSMauro Carvalho Chehab return entity; 279*0c0d06caSMauro Carvalho Chehab } 280*0c0d06caSMauro Carvalho Chehab 281*0c0d06caSMauro Carvalho Chehab return NULL; 282*0c0d06caSMauro Carvalho Chehab } 283*0c0d06caSMauro Carvalho Chehab 284*0c0d06caSMauro Carvalho Chehab static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, 285*0c0d06caSMauro Carvalho Chehab int id, struct uvc_entity *entity) 286*0c0d06caSMauro Carvalho Chehab { 287*0c0d06caSMauro Carvalho Chehab unsigned int i; 288*0c0d06caSMauro Carvalho Chehab 289*0c0d06caSMauro Carvalho Chehab if (entity == NULL) 290*0c0d06caSMauro Carvalho Chehab entity = list_entry(&dev->entities, struct uvc_entity, list); 291*0c0d06caSMauro Carvalho Chehab 292*0c0d06caSMauro Carvalho Chehab list_for_each_entry_continue(entity, &dev->entities, list) { 293*0c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->bNrInPins; ++i) 294*0c0d06caSMauro Carvalho Chehab if (entity->baSourceID[i] == id) 295*0c0d06caSMauro Carvalho Chehab return entity; 296*0c0d06caSMauro Carvalho Chehab } 297*0c0d06caSMauro Carvalho Chehab 298*0c0d06caSMauro Carvalho Chehab return NULL; 299*0c0d06caSMauro Carvalho Chehab } 300*0c0d06caSMauro Carvalho Chehab 301*0c0d06caSMauro Carvalho Chehab static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id) 302*0c0d06caSMauro Carvalho Chehab { 303*0c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 304*0c0d06caSMauro Carvalho Chehab 305*0c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 306*0c0d06caSMauro Carvalho Chehab if (stream->header.bTerminalLink == id) 307*0c0d06caSMauro Carvalho Chehab return stream; 308*0c0d06caSMauro Carvalho Chehab } 309*0c0d06caSMauro Carvalho Chehab 310*0c0d06caSMauro Carvalho Chehab return NULL; 311*0c0d06caSMauro Carvalho Chehab } 312*0c0d06caSMauro Carvalho Chehab 313*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 314*0c0d06caSMauro Carvalho Chehab * Descriptors parsing 315*0c0d06caSMauro Carvalho Chehab */ 316*0c0d06caSMauro Carvalho Chehab 317*0c0d06caSMauro Carvalho Chehab static int uvc_parse_format(struct uvc_device *dev, 318*0c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming, struct uvc_format *format, 319*0c0d06caSMauro Carvalho Chehab __u32 **intervals, unsigned char *buffer, int buflen) 320*0c0d06caSMauro Carvalho Chehab { 321*0c0d06caSMauro Carvalho Chehab struct usb_interface *intf = streaming->intf; 322*0c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = intf->cur_altsetting; 323*0c0d06caSMauro Carvalho Chehab struct uvc_format_desc *fmtdesc; 324*0c0d06caSMauro Carvalho Chehab struct uvc_frame *frame; 325*0c0d06caSMauro Carvalho Chehab const unsigned char *start = buffer; 326*0c0d06caSMauro Carvalho Chehab unsigned int interval; 327*0c0d06caSMauro Carvalho Chehab unsigned int i, n; 328*0c0d06caSMauro Carvalho Chehab __u8 ftype; 329*0c0d06caSMauro Carvalho Chehab 330*0c0d06caSMauro Carvalho Chehab format->type = buffer[2]; 331*0c0d06caSMauro Carvalho Chehab format->index = buffer[3]; 332*0c0d06caSMauro Carvalho Chehab 333*0c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 334*0c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 335*0c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 336*0c0d06caSMauro Carvalho Chehab n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28; 337*0c0d06caSMauro Carvalho Chehab if (buflen < n) { 338*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 339*0c0d06caSMauro Carvalho Chehab "interface %d FORMAT error\n", 340*0c0d06caSMauro Carvalho Chehab dev->udev->devnum, 341*0c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 342*0c0d06caSMauro Carvalho Chehab return -EINVAL; 343*0c0d06caSMauro Carvalho Chehab } 344*0c0d06caSMauro Carvalho Chehab 345*0c0d06caSMauro Carvalho Chehab /* Find the format descriptor from its GUID. */ 346*0c0d06caSMauro Carvalho Chehab fmtdesc = uvc_format_by_guid(&buffer[5]); 347*0c0d06caSMauro Carvalho Chehab 348*0c0d06caSMauro Carvalho Chehab if (fmtdesc != NULL) { 349*0c0d06caSMauro Carvalho Chehab strlcpy(format->name, fmtdesc->name, 350*0c0d06caSMauro Carvalho Chehab sizeof format->name); 351*0c0d06caSMauro Carvalho Chehab format->fcc = fmtdesc->fcc; 352*0c0d06caSMauro Carvalho Chehab } else { 353*0c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "Unknown video format %pUl\n", 354*0c0d06caSMauro Carvalho Chehab &buffer[5]); 355*0c0d06caSMauro Carvalho Chehab snprintf(format->name, sizeof(format->name), "%pUl\n", 356*0c0d06caSMauro Carvalho Chehab &buffer[5]); 357*0c0d06caSMauro Carvalho Chehab format->fcc = 0; 358*0c0d06caSMauro Carvalho Chehab } 359*0c0d06caSMauro Carvalho Chehab 360*0c0d06caSMauro Carvalho Chehab format->bpp = buffer[21]; 361*0c0d06caSMauro Carvalho Chehab if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) { 362*0c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_UNCOMPRESSED; 363*0c0d06caSMauro Carvalho Chehab } else { 364*0c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_FRAME_BASED; 365*0c0d06caSMauro Carvalho Chehab if (buffer[27]) 366*0c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED; 367*0c0d06caSMauro Carvalho Chehab } 368*0c0d06caSMauro Carvalho Chehab break; 369*0c0d06caSMauro Carvalho Chehab 370*0c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 371*0c0d06caSMauro Carvalho Chehab if (buflen < 11) { 372*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 373*0c0d06caSMauro Carvalho Chehab "interface %d FORMAT error\n", 374*0c0d06caSMauro Carvalho Chehab dev->udev->devnum, 375*0c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 376*0c0d06caSMauro Carvalho Chehab return -EINVAL; 377*0c0d06caSMauro Carvalho Chehab } 378*0c0d06caSMauro Carvalho Chehab 379*0c0d06caSMauro Carvalho Chehab strlcpy(format->name, "MJPEG", sizeof format->name); 380*0c0d06caSMauro Carvalho Chehab format->fcc = V4L2_PIX_FMT_MJPEG; 381*0c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED; 382*0c0d06caSMauro Carvalho Chehab format->bpp = 0; 383*0c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_MJPEG; 384*0c0d06caSMauro Carvalho Chehab break; 385*0c0d06caSMauro Carvalho Chehab 386*0c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 387*0c0d06caSMauro Carvalho Chehab if (buflen < 9) { 388*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 389*0c0d06caSMauro Carvalho Chehab "interface %d FORMAT error\n", 390*0c0d06caSMauro Carvalho Chehab dev->udev->devnum, 391*0c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 392*0c0d06caSMauro Carvalho Chehab return -EINVAL; 393*0c0d06caSMauro Carvalho Chehab } 394*0c0d06caSMauro Carvalho Chehab 395*0c0d06caSMauro Carvalho Chehab switch (buffer[8] & 0x7f) { 396*0c0d06caSMauro Carvalho Chehab case 0: 397*0c0d06caSMauro Carvalho Chehab strlcpy(format->name, "SD-DV", sizeof format->name); 398*0c0d06caSMauro Carvalho Chehab break; 399*0c0d06caSMauro Carvalho Chehab case 1: 400*0c0d06caSMauro Carvalho Chehab strlcpy(format->name, "SDL-DV", sizeof format->name); 401*0c0d06caSMauro Carvalho Chehab break; 402*0c0d06caSMauro Carvalho Chehab case 2: 403*0c0d06caSMauro Carvalho Chehab strlcpy(format->name, "HD-DV", sizeof format->name); 404*0c0d06caSMauro Carvalho Chehab break; 405*0c0d06caSMauro Carvalho Chehab default: 406*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 407*0c0d06caSMauro Carvalho Chehab "interface %d: unknown DV format %u\n", 408*0c0d06caSMauro Carvalho Chehab dev->udev->devnum, 409*0c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, buffer[8]); 410*0c0d06caSMauro Carvalho Chehab return -EINVAL; 411*0c0d06caSMauro Carvalho Chehab } 412*0c0d06caSMauro Carvalho Chehab 413*0c0d06caSMauro Carvalho Chehab strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz", 414*0c0d06caSMauro Carvalho Chehab sizeof format->name); 415*0c0d06caSMauro Carvalho Chehab 416*0c0d06caSMauro Carvalho Chehab format->fcc = V4L2_PIX_FMT_DV; 417*0c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM; 418*0c0d06caSMauro Carvalho Chehab format->bpp = 0; 419*0c0d06caSMauro Carvalho Chehab ftype = 0; 420*0c0d06caSMauro Carvalho Chehab 421*0c0d06caSMauro Carvalho Chehab /* Create a dummy frame descriptor. */ 422*0c0d06caSMauro Carvalho Chehab frame = &format->frame[0]; 423*0c0d06caSMauro Carvalho Chehab memset(&format->frame[0], 0, sizeof format->frame[0]); 424*0c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = 1; 425*0c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 1; 426*0c0d06caSMauro Carvalho Chehab frame->dwFrameInterval = *intervals; 427*0c0d06caSMauro Carvalho Chehab *(*intervals)++ = 1; 428*0c0d06caSMauro Carvalho Chehab format->nframes = 1; 429*0c0d06caSMauro Carvalho Chehab break; 430*0c0d06caSMauro Carvalho Chehab 431*0c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MPEG2TS: 432*0c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_STREAM_BASED: 433*0c0d06caSMauro Carvalho Chehab /* Not supported yet. */ 434*0c0d06caSMauro Carvalho Chehab default: 435*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 436*0c0d06caSMauro Carvalho Chehab "interface %d unsupported format %u\n", 437*0c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber, 438*0c0d06caSMauro Carvalho Chehab buffer[2]); 439*0c0d06caSMauro Carvalho Chehab return -EINVAL; 440*0c0d06caSMauro Carvalho Chehab } 441*0c0d06caSMauro Carvalho Chehab 442*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name); 443*0c0d06caSMauro Carvalho Chehab 444*0c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 445*0c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 446*0c0d06caSMauro Carvalho Chehab 447*0c0d06caSMauro Carvalho Chehab /* Parse the frame descriptors. Only uncompressed, MJPEG and frame 448*0c0d06caSMauro Carvalho Chehab * based formats have frame descriptors. 449*0c0d06caSMauro Carvalho Chehab */ 450*0c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 451*0c0d06caSMauro Carvalho Chehab buffer[2] == ftype) { 452*0c0d06caSMauro Carvalho Chehab frame = &format->frame[format->nframes]; 453*0c0d06caSMauro Carvalho Chehab if (ftype != UVC_VS_FRAME_FRAME_BASED) 454*0c0d06caSMauro Carvalho Chehab n = buflen > 25 ? buffer[25] : 0; 455*0c0d06caSMauro Carvalho Chehab else 456*0c0d06caSMauro Carvalho Chehab n = buflen > 21 ? buffer[21] : 0; 457*0c0d06caSMauro Carvalho Chehab 458*0c0d06caSMauro Carvalho Chehab n = n ? n : 3; 459*0c0d06caSMauro Carvalho Chehab 460*0c0d06caSMauro Carvalho Chehab if (buflen < 26 + 4*n) { 461*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 462*0c0d06caSMauro Carvalho Chehab "interface %d FRAME error\n", dev->udev->devnum, 463*0c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 464*0c0d06caSMauro Carvalho Chehab return -EINVAL; 465*0c0d06caSMauro Carvalho Chehab } 466*0c0d06caSMauro Carvalho Chehab 467*0c0d06caSMauro Carvalho Chehab frame->bFrameIndex = buffer[3]; 468*0c0d06caSMauro Carvalho Chehab frame->bmCapabilities = buffer[4]; 469*0c0d06caSMauro Carvalho Chehab frame->wWidth = get_unaligned_le16(&buffer[5]); 470*0c0d06caSMauro Carvalho Chehab frame->wHeight = get_unaligned_le16(&buffer[7]); 471*0c0d06caSMauro Carvalho Chehab frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); 472*0c0d06caSMauro Carvalho Chehab frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); 473*0c0d06caSMauro Carvalho Chehab if (ftype != UVC_VS_FRAME_FRAME_BASED) { 474*0c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = 475*0c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[17]); 476*0c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 477*0c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[21]); 478*0c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = buffer[25]; 479*0c0d06caSMauro Carvalho Chehab } else { 480*0c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = 0; 481*0c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 482*0c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[17]); 483*0c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = buffer[21]; 484*0c0d06caSMauro Carvalho Chehab } 485*0c0d06caSMauro Carvalho Chehab frame->dwFrameInterval = *intervals; 486*0c0d06caSMauro Carvalho Chehab 487*0c0d06caSMauro Carvalho Chehab /* Several UVC chipsets screw up dwMaxVideoFrameBufferSize 488*0c0d06caSMauro Carvalho Chehab * completely. Observed behaviours range from setting the 489*0c0d06caSMauro Carvalho Chehab * value to 1.1x the actual frame size to hardwiring the 490*0c0d06caSMauro Carvalho Chehab * 16 low bits to 0. This results in a higher than necessary 491*0c0d06caSMauro Carvalho Chehab * memory usage as well as a wrong image size information. For 492*0c0d06caSMauro Carvalho Chehab * uncompressed formats this can be fixed by computing the 493*0c0d06caSMauro Carvalho Chehab * value from the frame size. 494*0c0d06caSMauro Carvalho Chehab */ 495*0c0d06caSMauro Carvalho Chehab if (!(format->flags & UVC_FMT_FLAG_COMPRESSED)) 496*0c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = format->bpp 497*0c0d06caSMauro Carvalho Chehab * frame->wWidth * frame->wHeight / 8; 498*0c0d06caSMauro Carvalho Chehab 499*0c0d06caSMauro Carvalho Chehab /* Some bogus devices report dwMinFrameInterval equal to 500*0c0d06caSMauro Carvalho Chehab * dwMaxFrameInterval and have dwFrameIntervalStep set to 501*0c0d06caSMauro Carvalho Chehab * zero. Setting all null intervals to 1 fixes the problem and 502*0c0d06caSMauro Carvalho Chehab * some other divisions by zero that could happen. 503*0c0d06caSMauro Carvalho Chehab */ 504*0c0d06caSMauro Carvalho Chehab for (i = 0; i < n; ++i) { 505*0c0d06caSMauro Carvalho Chehab interval = get_unaligned_le32(&buffer[26+4*i]); 506*0c0d06caSMauro Carvalho Chehab *(*intervals)++ = interval ? interval : 1; 507*0c0d06caSMauro Carvalho Chehab } 508*0c0d06caSMauro Carvalho Chehab 509*0c0d06caSMauro Carvalho Chehab /* Make sure that the default frame interval stays between 510*0c0d06caSMauro Carvalho Chehab * the boundaries. 511*0c0d06caSMauro Carvalho Chehab */ 512*0c0d06caSMauro Carvalho Chehab n -= frame->bFrameIntervalType ? 1 : 2; 513*0c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 514*0c0d06caSMauro Carvalho Chehab min(frame->dwFrameInterval[n], 515*0c0d06caSMauro Carvalho Chehab max(frame->dwFrameInterval[0], 516*0c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval)); 517*0c0d06caSMauro Carvalho Chehab 518*0c0d06caSMauro Carvalho Chehab if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) { 519*0c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = 1; 520*0c0d06caSMauro Carvalho Chehab frame->dwFrameInterval[0] = 521*0c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval; 522*0c0d06caSMauro Carvalho Chehab } 523*0c0d06caSMauro Carvalho Chehab 524*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n", 525*0c0d06caSMauro Carvalho Chehab frame->wWidth, frame->wHeight, 526*0c0d06caSMauro Carvalho Chehab 10000000/frame->dwDefaultFrameInterval, 527*0c0d06caSMauro Carvalho Chehab (100000000/frame->dwDefaultFrameInterval)%10); 528*0c0d06caSMauro Carvalho Chehab 529*0c0d06caSMauro Carvalho Chehab format->nframes++; 530*0c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 531*0c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 532*0c0d06caSMauro Carvalho Chehab } 533*0c0d06caSMauro Carvalho Chehab 534*0c0d06caSMauro Carvalho Chehab if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 535*0c0d06caSMauro Carvalho Chehab buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { 536*0c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 537*0c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 538*0c0d06caSMauro Carvalho Chehab } 539*0c0d06caSMauro Carvalho Chehab 540*0c0d06caSMauro Carvalho Chehab if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 541*0c0d06caSMauro Carvalho Chehab buffer[2] == UVC_VS_COLORFORMAT) { 542*0c0d06caSMauro Carvalho Chehab if (buflen < 6) { 543*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 544*0c0d06caSMauro Carvalho Chehab "interface %d COLORFORMAT error\n", 545*0c0d06caSMauro Carvalho Chehab dev->udev->devnum, 546*0c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 547*0c0d06caSMauro Carvalho Chehab return -EINVAL; 548*0c0d06caSMauro Carvalho Chehab } 549*0c0d06caSMauro Carvalho Chehab 550*0c0d06caSMauro Carvalho Chehab format->colorspace = uvc_colorspace(buffer[3]); 551*0c0d06caSMauro Carvalho Chehab 552*0c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 553*0c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 554*0c0d06caSMauro Carvalho Chehab } 555*0c0d06caSMauro Carvalho Chehab 556*0c0d06caSMauro Carvalho Chehab return buffer - start; 557*0c0d06caSMauro Carvalho Chehab } 558*0c0d06caSMauro Carvalho Chehab 559*0c0d06caSMauro Carvalho Chehab static int uvc_parse_streaming(struct uvc_device *dev, 560*0c0d06caSMauro Carvalho Chehab struct usb_interface *intf) 561*0c0d06caSMauro Carvalho Chehab { 562*0c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming = NULL; 563*0c0d06caSMauro Carvalho Chehab struct uvc_format *format; 564*0c0d06caSMauro Carvalho Chehab struct uvc_frame *frame; 565*0c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = &intf->altsetting[0]; 566*0c0d06caSMauro Carvalho Chehab unsigned char *_buffer, *buffer = alts->extra; 567*0c0d06caSMauro Carvalho Chehab int _buflen, buflen = alts->extralen; 568*0c0d06caSMauro Carvalho Chehab unsigned int nformats = 0, nframes = 0, nintervals = 0; 569*0c0d06caSMauro Carvalho Chehab unsigned int size, i, n, p; 570*0c0d06caSMauro Carvalho Chehab __u32 *interval; 571*0c0d06caSMauro Carvalho Chehab __u16 psize; 572*0c0d06caSMauro Carvalho Chehab int ret = -EINVAL; 573*0c0d06caSMauro Carvalho Chehab 574*0c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass 575*0c0d06caSMauro Carvalho Chehab != UVC_SC_VIDEOSTREAMING) { 576*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a " 577*0c0d06caSMauro Carvalho Chehab "video streaming interface\n", dev->udev->devnum, 578*0c0d06caSMauro Carvalho Chehab intf->altsetting[0].desc.bInterfaceNumber); 579*0c0d06caSMauro Carvalho Chehab return -EINVAL; 580*0c0d06caSMauro Carvalho Chehab } 581*0c0d06caSMauro Carvalho Chehab 582*0c0d06caSMauro Carvalho Chehab if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) { 583*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already " 584*0c0d06caSMauro Carvalho Chehab "claimed\n", dev->udev->devnum, 585*0c0d06caSMauro Carvalho Chehab intf->altsetting[0].desc.bInterfaceNumber); 586*0c0d06caSMauro Carvalho Chehab return -EINVAL; 587*0c0d06caSMauro Carvalho Chehab } 588*0c0d06caSMauro Carvalho Chehab 589*0c0d06caSMauro Carvalho Chehab streaming = kzalloc(sizeof *streaming, GFP_KERNEL); 590*0c0d06caSMauro Carvalho Chehab if (streaming == NULL) { 591*0c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, intf); 592*0c0d06caSMauro Carvalho Chehab return -EINVAL; 593*0c0d06caSMauro Carvalho Chehab } 594*0c0d06caSMauro Carvalho Chehab 595*0c0d06caSMauro Carvalho Chehab mutex_init(&streaming->mutex); 596*0c0d06caSMauro Carvalho Chehab streaming->dev = dev; 597*0c0d06caSMauro Carvalho Chehab streaming->intf = usb_get_intf(intf); 598*0c0d06caSMauro Carvalho Chehab streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; 599*0c0d06caSMauro Carvalho Chehab 600*0c0d06caSMauro Carvalho Chehab /* The Pico iMage webcam has its class-specific interface descriptors 601*0c0d06caSMauro Carvalho Chehab * after the endpoint descriptors. 602*0c0d06caSMauro Carvalho Chehab */ 603*0c0d06caSMauro Carvalho Chehab if (buflen == 0) { 604*0c0d06caSMauro Carvalho Chehab for (i = 0; i < alts->desc.bNumEndpoints; ++i) { 605*0c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep = &alts->endpoint[i]; 606*0c0d06caSMauro Carvalho Chehab 607*0c0d06caSMauro Carvalho Chehab if (ep->extralen == 0) 608*0c0d06caSMauro Carvalho Chehab continue; 609*0c0d06caSMauro Carvalho Chehab 610*0c0d06caSMauro Carvalho Chehab if (ep->extralen > 2 && 611*0c0d06caSMauro Carvalho Chehab ep->extra[1] == USB_DT_CS_INTERFACE) { 612*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "trying extra data " 613*0c0d06caSMauro Carvalho Chehab "from endpoint %u.\n", i); 614*0c0d06caSMauro Carvalho Chehab buffer = alts->endpoint[i].extra; 615*0c0d06caSMauro Carvalho Chehab buflen = alts->endpoint[i].extralen; 616*0c0d06caSMauro Carvalho Chehab break; 617*0c0d06caSMauro Carvalho Chehab } 618*0c0d06caSMauro Carvalho Chehab } 619*0c0d06caSMauro Carvalho Chehab } 620*0c0d06caSMauro Carvalho Chehab 621*0c0d06caSMauro Carvalho Chehab /* Skip the standard interface descriptors. */ 622*0c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) { 623*0c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 624*0c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 625*0c0d06caSMauro Carvalho Chehab } 626*0c0d06caSMauro Carvalho Chehab 627*0c0d06caSMauro Carvalho Chehab if (buflen <= 2) { 628*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming " 629*0c0d06caSMauro Carvalho Chehab "interface descriptors found.\n"); 630*0c0d06caSMauro Carvalho Chehab goto error; 631*0c0d06caSMauro Carvalho Chehab } 632*0c0d06caSMauro Carvalho Chehab 633*0c0d06caSMauro Carvalho Chehab /* Parse the header descriptor. */ 634*0c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 635*0c0d06caSMauro Carvalho Chehab case UVC_VS_OUTPUT_HEADER: 636*0c0d06caSMauro Carvalho Chehab streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 637*0c0d06caSMauro Carvalho Chehab size = 9; 638*0c0d06caSMauro Carvalho Chehab break; 639*0c0d06caSMauro Carvalho Chehab 640*0c0d06caSMauro Carvalho Chehab case UVC_VS_INPUT_HEADER: 641*0c0d06caSMauro Carvalho Chehab streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 642*0c0d06caSMauro Carvalho Chehab size = 13; 643*0c0d06caSMauro Carvalho Chehab break; 644*0c0d06caSMauro Carvalho Chehab 645*0c0d06caSMauro Carvalho Chehab default: 646*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " 647*0c0d06caSMauro Carvalho Chehab "%d HEADER descriptor not found.\n", dev->udev->devnum, 648*0c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 649*0c0d06caSMauro Carvalho Chehab goto error; 650*0c0d06caSMauro Carvalho Chehab } 651*0c0d06caSMauro Carvalho Chehab 652*0c0d06caSMauro Carvalho Chehab p = buflen >= 4 ? buffer[3] : 0; 653*0c0d06caSMauro Carvalho Chehab n = buflen >= size ? buffer[size-1] : 0; 654*0c0d06caSMauro Carvalho Chehab 655*0c0d06caSMauro Carvalho Chehab if (buflen < size + p*n) { 656*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 657*0c0d06caSMauro Carvalho Chehab "interface %d HEADER descriptor is invalid.\n", 658*0c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber); 659*0c0d06caSMauro Carvalho Chehab goto error; 660*0c0d06caSMauro Carvalho Chehab } 661*0c0d06caSMauro Carvalho Chehab 662*0c0d06caSMauro Carvalho Chehab streaming->header.bNumFormats = p; 663*0c0d06caSMauro Carvalho Chehab streaming->header.bEndpointAddress = buffer[6]; 664*0c0d06caSMauro Carvalho Chehab if (buffer[2] == UVC_VS_INPUT_HEADER) { 665*0c0d06caSMauro Carvalho Chehab streaming->header.bmInfo = buffer[7]; 666*0c0d06caSMauro Carvalho Chehab streaming->header.bTerminalLink = buffer[8]; 667*0c0d06caSMauro Carvalho Chehab streaming->header.bStillCaptureMethod = buffer[9]; 668*0c0d06caSMauro Carvalho Chehab streaming->header.bTriggerSupport = buffer[10]; 669*0c0d06caSMauro Carvalho Chehab streaming->header.bTriggerUsage = buffer[11]; 670*0c0d06caSMauro Carvalho Chehab } else { 671*0c0d06caSMauro Carvalho Chehab streaming->header.bTerminalLink = buffer[7]; 672*0c0d06caSMauro Carvalho Chehab } 673*0c0d06caSMauro Carvalho Chehab streaming->header.bControlSize = n; 674*0c0d06caSMauro Carvalho Chehab 675*0c0d06caSMauro Carvalho Chehab streaming->header.bmaControls = kmemdup(&buffer[size], p * n, 676*0c0d06caSMauro Carvalho Chehab GFP_KERNEL); 677*0c0d06caSMauro Carvalho Chehab if (streaming->header.bmaControls == NULL) { 678*0c0d06caSMauro Carvalho Chehab ret = -ENOMEM; 679*0c0d06caSMauro Carvalho Chehab goto error; 680*0c0d06caSMauro Carvalho Chehab } 681*0c0d06caSMauro Carvalho Chehab 682*0c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 683*0c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 684*0c0d06caSMauro Carvalho Chehab 685*0c0d06caSMauro Carvalho Chehab _buffer = buffer; 686*0c0d06caSMauro Carvalho Chehab _buflen = buflen; 687*0c0d06caSMauro Carvalho Chehab 688*0c0d06caSMauro Carvalho Chehab /* Count the format and frame descriptors. */ 689*0c0d06caSMauro Carvalho Chehab while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) { 690*0c0d06caSMauro Carvalho Chehab switch (_buffer[2]) { 691*0c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 692*0c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 693*0c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 694*0c0d06caSMauro Carvalho Chehab nformats++; 695*0c0d06caSMauro Carvalho Chehab break; 696*0c0d06caSMauro Carvalho Chehab 697*0c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 698*0c0d06caSMauro Carvalho Chehab /* DV format has no frame descriptor. We will create a 699*0c0d06caSMauro Carvalho Chehab * dummy frame descriptor with a dummy frame interval. 700*0c0d06caSMauro Carvalho Chehab */ 701*0c0d06caSMauro Carvalho Chehab nformats++; 702*0c0d06caSMauro Carvalho Chehab nframes++; 703*0c0d06caSMauro Carvalho Chehab nintervals++; 704*0c0d06caSMauro Carvalho Chehab break; 705*0c0d06caSMauro Carvalho Chehab 706*0c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MPEG2TS: 707*0c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_STREAM_BASED: 708*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 709*0c0d06caSMauro Carvalho Chehab "interface %d FORMAT %u is not supported.\n", 710*0c0d06caSMauro Carvalho Chehab dev->udev->devnum, 711*0c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, _buffer[2]); 712*0c0d06caSMauro Carvalho Chehab break; 713*0c0d06caSMauro Carvalho Chehab 714*0c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_UNCOMPRESSED: 715*0c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_MJPEG: 716*0c0d06caSMauro Carvalho Chehab nframes++; 717*0c0d06caSMauro Carvalho Chehab if (_buflen > 25) 718*0c0d06caSMauro Carvalho Chehab nintervals += _buffer[25] ? _buffer[25] : 3; 719*0c0d06caSMauro Carvalho Chehab break; 720*0c0d06caSMauro Carvalho Chehab 721*0c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_FRAME_BASED: 722*0c0d06caSMauro Carvalho Chehab nframes++; 723*0c0d06caSMauro Carvalho Chehab if (_buflen > 21) 724*0c0d06caSMauro Carvalho Chehab nintervals += _buffer[21] ? _buffer[21] : 3; 725*0c0d06caSMauro Carvalho Chehab break; 726*0c0d06caSMauro Carvalho Chehab } 727*0c0d06caSMauro Carvalho Chehab 728*0c0d06caSMauro Carvalho Chehab _buflen -= _buffer[0]; 729*0c0d06caSMauro Carvalho Chehab _buffer += _buffer[0]; 730*0c0d06caSMauro Carvalho Chehab } 731*0c0d06caSMauro Carvalho Chehab 732*0c0d06caSMauro Carvalho Chehab if (nformats == 0) { 733*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " 734*0c0d06caSMauro Carvalho Chehab "%d has no supported formats defined.\n", 735*0c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber); 736*0c0d06caSMauro Carvalho Chehab goto error; 737*0c0d06caSMauro Carvalho Chehab } 738*0c0d06caSMauro Carvalho Chehab 739*0c0d06caSMauro Carvalho Chehab size = nformats * sizeof *format + nframes * sizeof *frame 740*0c0d06caSMauro Carvalho Chehab + nintervals * sizeof *interval; 741*0c0d06caSMauro Carvalho Chehab format = kzalloc(size, GFP_KERNEL); 742*0c0d06caSMauro Carvalho Chehab if (format == NULL) { 743*0c0d06caSMauro Carvalho Chehab ret = -ENOMEM; 744*0c0d06caSMauro Carvalho Chehab goto error; 745*0c0d06caSMauro Carvalho Chehab } 746*0c0d06caSMauro Carvalho Chehab 747*0c0d06caSMauro Carvalho Chehab frame = (struct uvc_frame *)&format[nformats]; 748*0c0d06caSMauro Carvalho Chehab interval = (__u32 *)&frame[nframes]; 749*0c0d06caSMauro Carvalho Chehab 750*0c0d06caSMauro Carvalho Chehab streaming->format = format; 751*0c0d06caSMauro Carvalho Chehab streaming->nformats = nformats; 752*0c0d06caSMauro Carvalho Chehab 753*0c0d06caSMauro Carvalho Chehab /* Parse the format descriptors. */ 754*0c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) { 755*0c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 756*0c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 757*0c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 758*0c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 759*0c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 760*0c0d06caSMauro Carvalho Chehab format->frame = frame; 761*0c0d06caSMauro Carvalho Chehab ret = uvc_parse_format(dev, streaming, format, 762*0c0d06caSMauro Carvalho Chehab &interval, buffer, buflen); 763*0c0d06caSMauro Carvalho Chehab if (ret < 0) 764*0c0d06caSMauro Carvalho Chehab goto error; 765*0c0d06caSMauro Carvalho Chehab 766*0c0d06caSMauro Carvalho Chehab frame += format->nframes; 767*0c0d06caSMauro Carvalho Chehab format++; 768*0c0d06caSMauro Carvalho Chehab 769*0c0d06caSMauro Carvalho Chehab buflen -= ret; 770*0c0d06caSMauro Carvalho Chehab buffer += ret; 771*0c0d06caSMauro Carvalho Chehab continue; 772*0c0d06caSMauro Carvalho Chehab 773*0c0d06caSMauro Carvalho Chehab default: 774*0c0d06caSMauro Carvalho Chehab break; 775*0c0d06caSMauro Carvalho Chehab } 776*0c0d06caSMauro Carvalho Chehab 777*0c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 778*0c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 779*0c0d06caSMauro Carvalho Chehab } 780*0c0d06caSMauro Carvalho Chehab 781*0c0d06caSMauro Carvalho Chehab if (buflen) 782*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " 783*0c0d06caSMauro Carvalho Chehab "%d has %u bytes of trailing descriptor garbage.\n", 784*0c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber, buflen); 785*0c0d06caSMauro Carvalho Chehab 786*0c0d06caSMauro Carvalho Chehab /* Parse the alternate settings to find the maximum bandwidth. */ 787*0c0d06caSMauro Carvalho Chehab for (i = 0; i < intf->num_altsetting; ++i) { 788*0c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep; 789*0c0d06caSMauro Carvalho Chehab alts = &intf->altsetting[i]; 790*0c0d06caSMauro Carvalho Chehab ep = uvc_find_endpoint(alts, 791*0c0d06caSMauro Carvalho Chehab streaming->header.bEndpointAddress); 792*0c0d06caSMauro Carvalho Chehab if (ep == NULL) 793*0c0d06caSMauro Carvalho Chehab continue; 794*0c0d06caSMauro Carvalho Chehab 795*0c0d06caSMauro Carvalho Chehab psize = le16_to_cpu(ep->desc.wMaxPacketSize); 796*0c0d06caSMauro Carvalho Chehab psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); 797*0c0d06caSMauro Carvalho Chehab if (psize > streaming->maxpsize) 798*0c0d06caSMauro Carvalho Chehab streaming->maxpsize = psize; 799*0c0d06caSMauro Carvalho Chehab } 800*0c0d06caSMauro Carvalho Chehab 801*0c0d06caSMauro Carvalho Chehab list_add_tail(&streaming->list, &dev->streams); 802*0c0d06caSMauro Carvalho Chehab return 0; 803*0c0d06caSMauro Carvalho Chehab 804*0c0d06caSMauro Carvalho Chehab error: 805*0c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, intf); 806*0c0d06caSMauro Carvalho Chehab usb_put_intf(intf); 807*0c0d06caSMauro Carvalho Chehab kfree(streaming->format); 808*0c0d06caSMauro Carvalho Chehab kfree(streaming->header.bmaControls); 809*0c0d06caSMauro Carvalho Chehab kfree(streaming); 810*0c0d06caSMauro Carvalho Chehab return ret; 811*0c0d06caSMauro Carvalho Chehab } 812*0c0d06caSMauro Carvalho Chehab 813*0c0d06caSMauro Carvalho Chehab static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, 814*0c0d06caSMauro Carvalho Chehab unsigned int num_pads, unsigned int extra_size) 815*0c0d06caSMauro Carvalho Chehab { 816*0c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 817*0c0d06caSMauro Carvalho Chehab unsigned int num_inputs; 818*0c0d06caSMauro Carvalho Chehab unsigned int size; 819*0c0d06caSMauro Carvalho Chehab unsigned int i; 820*0c0d06caSMauro Carvalho Chehab 821*0c0d06caSMauro Carvalho Chehab extra_size = ALIGN(extra_size, sizeof(*entity->pads)); 822*0c0d06caSMauro Carvalho Chehab num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1; 823*0c0d06caSMauro Carvalho Chehab size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads 824*0c0d06caSMauro Carvalho Chehab + num_inputs; 825*0c0d06caSMauro Carvalho Chehab entity = kzalloc(size, GFP_KERNEL); 826*0c0d06caSMauro Carvalho Chehab if (entity == NULL) 827*0c0d06caSMauro Carvalho Chehab return NULL; 828*0c0d06caSMauro Carvalho Chehab 829*0c0d06caSMauro Carvalho Chehab entity->id = id; 830*0c0d06caSMauro Carvalho Chehab entity->type = type; 831*0c0d06caSMauro Carvalho Chehab 832*0c0d06caSMauro Carvalho Chehab entity->num_links = 0; 833*0c0d06caSMauro Carvalho Chehab entity->num_pads = num_pads; 834*0c0d06caSMauro Carvalho Chehab entity->pads = ((void *)(entity + 1)) + extra_size; 835*0c0d06caSMauro Carvalho Chehab 836*0c0d06caSMauro Carvalho Chehab for (i = 0; i < num_inputs; ++i) 837*0c0d06caSMauro Carvalho Chehab entity->pads[i].flags = MEDIA_PAD_FL_SINK; 838*0c0d06caSMauro Carvalho Chehab if (!UVC_ENTITY_IS_OTERM(entity)) 839*0c0d06caSMauro Carvalho Chehab entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE; 840*0c0d06caSMauro Carvalho Chehab 841*0c0d06caSMauro Carvalho Chehab entity->bNrInPins = num_inputs; 842*0c0d06caSMauro Carvalho Chehab entity->baSourceID = (__u8 *)(&entity->pads[num_pads]); 843*0c0d06caSMauro Carvalho Chehab 844*0c0d06caSMauro Carvalho Chehab return entity; 845*0c0d06caSMauro Carvalho Chehab } 846*0c0d06caSMauro Carvalho Chehab 847*0c0d06caSMauro Carvalho Chehab /* Parse vendor-specific extensions. */ 848*0c0d06caSMauro Carvalho Chehab static int uvc_parse_vendor_control(struct uvc_device *dev, 849*0c0d06caSMauro Carvalho Chehab const unsigned char *buffer, int buflen) 850*0c0d06caSMauro Carvalho Chehab { 851*0c0d06caSMauro Carvalho Chehab struct usb_device *udev = dev->udev; 852*0c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 853*0c0d06caSMauro Carvalho Chehab struct uvc_entity *unit; 854*0c0d06caSMauro Carvalho Chehab unsigned int n, p; 855*0c0d06caSMauro Carvalho Chehab int handled = 0; 856*0c0d06caSMauro Carvalho Chehab 857*0c0d06caSMauro Carvalho Chehab switch (le16_to_cpu(dev->udev->descriptor.idVendor)) { 858*0c0d06caSMauro Carvalho Chehab case 0x046d: /* Logitech */ 859*0c0d06caSMauro Carvalho Chehab if (buffer[1] != 0x41 || buffer[2] != 0x01) 860*0c0d06caSMauro Carvalho Chehab break; 861*0c0d06caSMauro Carvalho Chehab 862*0c0d06caSMauro Carvalho Chehab /* Logitech implements several vendor specific functions 863*0c0d06caSMauro Carvalho Chehab * through vendor specific extension units (LXU). 864*0c0d06caSMauro Carvalho Chehab * 865*0c0d06caSMauro Carvalho Chehab * The LXU descriptors are similar to XU descriptors 866*0c0d06caSMauro Carvalho Chehab * (see "USB Device Video Class for Video Devices", section 867*0c0d06caSMauro Carvalho Chehab * 3.7.2.6 "Extension Unit Descriptor") with the following 868*0c0d06caSMauro Carvalho Chehab * differences: 869*0c0d06caSMauro Carvalho Chehab * 870*0c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 871*0c0d06caSMauro Carvalho Chehab * 0 bLength 1 Number 872*0c0d06caSMauro Carvalho Chehab * Size of this descriptor, in bytes: 24+p+n*2 873*0c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 874*0c0d06caSMauro Carvalho Chehab * 23+p+n bmControlsType N Bitmap 875*0c0d06caSMauro Carvalho Chehab * Individual bits in the set are defined: 876*0c0d06caSMauro Carvalho Chehab * 0: Absolute 877*0c0d06caSMauro Carvalho Chehab * 1: Relative 878*0c0d06caSMauro Carvalho Chehab * 879*0c0d06caSMauro Carvalho Chehab * This bitset is mapped exactly the same as bmControls. 880*0c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 881*0c0d06caSMauro Carvalho Chehab * 23+p+n*2 bReserved 1 Boolean 882*0c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 883*0c0d06caSMauro Carvalho Chehab * 24+p+n*2 iExtension 1 Index 884*0c0d06caSMauro Carvalho Chehab * Index of a string descriptor that describes this 885*0c0d06caSMauro Carvalho Chehab * extension unit. 886*0c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 887*0c0d06caSMauro Carvalho Chehab */ 888*0c0d06caSMauro Carvalho Chehab p = buflen >= 22 ? buffer[21] : 0; 889*0c0d06caSMauro Carvalho Chehab n = buflen >= 25 + p ? buffer[22+p] : 0; 890*0c0d06caSMauro Carvalho Chehab 891*0c0d06caSMauro Carvalho Chehab if (buflen < 25 + p + 2*n) { 892*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 893*0c0d06caSMauro Carvalho Chehab "interface %d EXTENSION_UNIT error\n", 894*0c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 895*0c0d06caSMauro Carvalho Chehab break; 896*0c0d06caSMauro Carvalho Chehab } 897*0c0d06caSMauro Carvalho Chehab 898*0c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3], 899*0c0d06caSMauro Carvalho Chehab p + 1, 2*n); 900*0c0d06caSMauro Carvalho Chehab if (unit == NULL) 901*0c0d06caSMauro Carvalho Chehab return -ENOMEM; 902*0c0d06caSMauro Carvalho Chehab 903*0c0d06caSMauro Carvalho Chehab memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); 904*0c0d06caSMauro Carvalho Chehab unit->extension.bNumControls = buffer[20]; 905*0c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[22], p); 906*0c0d06caSMauro Carvalho Chehab unit->extension.bControlSize = buffer[22+p]; 907*0c0d06caSMauro Carvalho Chehab unit->extension.bmControls = (__u8 *)unit + sizeof(*unit); 908*0c0d06caSMauro Carvalho Chehab unit->extension.bmControlsType = (__u8 *)unit + sizeof(*unit) 909*0c0d06caSMauro Carvalho Chehab + n; 910*0c0d06caSMauro Carvalho Chehab memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); 911*0c0d06caSMauro Carvalho Chehab 912*0c0d06caSMauro Carvalho Chehab if (buffer[24+p+2*n] != 0) 913*0c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[24+p+2*n], unit->name, 914*0c0d06caSMauro Carvalho Chehab sizeof unit->name); 915*0c0d06caSMauro Carvalho Chehab else 916*0c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Extension %u", buffer[3]); 917*0c0d06caSMauro Carvalho Chehab 918*0c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 919*0c0d06caSMauro Carvalho Chehab handled = 1; 920*0c0d06caSMauro Carvalho Chehab break; 921*0c0d06caSMauro Carvalho Chehab } 922*0c0d06caSMauro Carvalho Chehab 923*0c0d06caSMauro Carvalho Chehab return handled; 924*0c0d06caSMauro Carvalho Chehab } 925*0c0d06caSMauro Carvalho Chehab 926*0c0d06caSMauro Carvalho Chehab static int uvc_parse_standard_control(struct uvc_device *dev, 927*0c0d06caSMauro Carvalho Chehab const unsigned char *buffer, int buflen) 928*0c0d06caSMauro Carvalho Chehab { 929*0c0d06caSMauro Carvalho Chehab struct usb_device *udev = dev->udev; 930*0c0d06caSMauro Carvalho Chehab struct uvc_entity *unit, *term; 931*0c0d06caSMauro Carvalho Chehab struct usb_interface *intf; 932*0c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 933*0c0d06caSMauro Carvalho Chehab unsigned int i, n, p, len; 934*0c0d06caSMauro Carvalho Chehab __u16 type; 935*0c0d06caSMauro Carvalho Chehab 936*0c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 937*0c0d06caSMauro Carvalho Chehab case UVC_VC_HEADER: 938*0c0d06caSMauro Carvalho Chehab n = buflen >= 12 ? buffer[11] : 0; 939*0c0d06caSMauro Carvalho Chehab 940*0c0d06caSMauro Carvalho Chehab if (buflen < 12 || buflen < 12 + n) { 941*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 942*0c0d06caSMauro Carvalho Chehab "interface %d HEADER error\n", udev->devnum, 943*0c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 944*0c0d06caSMauro Carvalho Chehab return -EINVAL; 945*0c0d06caSMauro Carvalho Chehab } 946*0c0d06caSMauro Carvalho Chehab 947*0c0d06caSMauro Carvalho Chehab dev->uvc_version = get_unaligned_le16(&buffer[3]); 948*0c0d06caSMauro Carvalho Chehab dev->clock_frequency = get_unaligned_le32(&buffer[7]); 949*0c0d06caSMauro Carvalho Chehab 950*0c0d06caSMauro Carvalho Chehab /* Parse all USB Video Streaming interfaces. */ 951*0c0d06caSMauro Carvalho Chehab for (i = 0; i < n; ++i) { 952*0c0d06caSMauro Carvalho Chehab intf = usb_ifnum_to_if(udev, buffer[12+i]); 953*0c0d06caSMauro Carvalho Chehab if (intf == NULL) { 954*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d " 955*0c0d06caSMauro Carvalho Chehab "interface %d doesn't exists\n", 956*0c0d06caSMauro Carvalho Chehab udev->devnum, i); 957*0c0d06caSMauro Carvalho Chehab continue; 958*0c0d06caSMauro Carvalho Chehab } 959*0c0d06caSMauro Carvalho Chehab 960*0c0d06caSMauro Carvalho Chehab uvc_parse_streaming(dev, intf); 961*0c0d06caSMauro Carvalho Chehab } 962*0c0d06caSMauro Carvalho Chehab break; 963*0c0d06caSMauro Carvalho Chehab 964*0c0d06caSMauro Carvalho Chehab case UVC_VC_INPUT_TERMINAL: 965*0c0d06caSMauro Carvalho Chehab if (buflen < 8) { 966*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 967*0c0d06caSMauro Carvalho Chehab "interface %d INPUT_TERMINAL error\n", 968*0c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 969*0c0d06caSMauro Carvalho Chehab return -EINVAL; 970*0c0d06caSMauro Carvalho Chehab } 971*0c0d06caSMauro Carvalho Chehab 972*0c0d06caSMauro Carvalho Chehab /* Make sure the terminal type MSB is not null, otherwise it 973*0c0d06caSMauro Carvalho Chehab * could be confused with a unit. 974*0c0d06caSMauro Carvalho Chehab */ 975*0c0d06caSMauro Carvalho Chehab type = get_unaligned_le16(&buffer[4]); 976*0c0d06caSMauro Carvalho Chehab if ((type & 0xff00) == 0) { 977*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 978*0c0d06caSMauro Carvalho Chehab "interface %d INPUT_TERMINAL %d has invalid " 979*0c0d06caSMauro Carvalho Chehab "type 0x%04x, skipping\n", udev->devnum, 980*0c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, 981*0c0d06caSMauro Carvalho Chehab buffer[3], type); 982*0c0d06caSMauro Carvalho Chehab return 0; 983*0c0d06caSMauro Carvalho Chehab } 984*0c0d06caSMauro Carvalho Chehab 985*0c0d06caSMauro Carvalho Chehab n = 0; 986*0c0d06caSMauro Carvalho Chehab p = 0; 987*0c0d06caSMauro Carvalho Chehab len = 8; 988*0c0d06caSMauro Carvalho Chehab 989*0c0d06caSMauro Carvalho Chehab if (type == UVC_ITT_CAMERA) { 990*0c0d06caSMauro Carvalho Chehab n = buflen >= 15 ? buffer[14] : 0; 991*0c0d06caSMauro Carvalho Chehab len = 15; 992*0c0d06caSMauro Carvalho Chehab 993*0c0d06caSMauro Carvalho Chehab } else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) { 994*0c0d06caSMauro Carvalho Chehab n = buflen >= 9 ? buffer[8] : 0; 995*0c0d06caSMauro Carvalho Chehab p = buflen >= 10 + n ? buffer[9+n] : 0; 996*0c0d06caSMauro Carvalho Chehab len = 10; 997*0c0d06caSMauro Carvalho Chehab } 998*0c0d06caSMauro Carvalho Chehab 999*0c0d06caSMauro Carvalho Chehab if (buflen < len + n + p) { 1000*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 1001*0c0d06caSMauro Carvalho Chehab "interface %d INPUT_TERMINAL error\n", 1002*0c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 1003*0c0d06caSMauro Carvalho Chehab return -EINVAL; 1004*0c0d06caSMauro Carvalho Chehab } 1005*0c0d06caSMauro Carvalho Chehab 1006*0c0d06caSMauro Carvalho Chehab term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3], 1007*0c0d06caSMauro Carvalho Chehab 1, n + p); 1008*0c0d06caSMauro Carvalho Chehab if (term == NULL) 1009*0c0d06caSMauro Carvalho Chehab return -ENOMEM; 1010*0c0d06caSMauro Carvalho Chehab 1011*0c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) { 1012*0c0d06caSMauro Carvalho Chehab term->camera.bControlSize = n; 1013*0c0d06caSMauro Carvalho Chehab term->camera.bmControls = (__u8 *)term + sizeof *term; 1014*0c0d06caSMauro Carvalho Chehab term->camera.wObjectiveFocalLengthMin = 1015*0c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[8]); 1016*0c0d06caSMauro Carvalho Chehab term->camera.wObjectiveFocalLengthMax = 1017*0c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[10]); 1018*0c0d06caSMauro Carvalho Chehab term->camera.wOcularFocalLength = 1019*0c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[12]); 1020*0c0d06caSMauro Carvalho Chehab memcpy(term->camera.bmControls, &buffer[15], n); 1021*0c0d06caSMauro Carvalho Chehab } else if (UVC_ENTITY_TYPE(term) == 1022*0c0d06caSMauro Carvalho Chehab UVC_ITT_MEDIA_TRANSPORT_INPUT) { 1023*0c0d06caSMauro Carvalho Chehab term->media.bControlSize = n; 1024*0c0d06caSMauro Carvalho Chehab term->media.bmControls = (__u8 *)term + sizeof *term; 1025*0c0d06caSMauro Carvalho Chehab term->media.bTransportModeSize = p; 1026*0c0d06caSMauro Carvalho Chehab term->media.bmTransportModes = (__u8 *)term 1027*0c0d06caSMauro Carvalho Chehab + sizeof *term + n; 1028*0c0d06caSMauro Carvalho Chehab memcpy(term->media.bmControls, &buffer[9], n); 1029*0c0d06caSMauro Carvalho Chehab memcpy(term->media.bmTransportModes, &buffer[10+n], p); 1030*0c0d06caSMauro Carvalho Chehab } 1031*0c0d06caSMauro Carvalho Chehab 1032*0c0d06caSMauro Carvalho Chehab if (buffer[7] != 0) 1033*0c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[7], term->name, 1034*0c0d06caSMauro Carvalho Chehab sizeof term->name); 1035*0c0d06caSMauro Carvalho Chehab else if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) 1036*0c0d06caSMauro Carvalho Chehab sprintf(term->name, "Camera %u", buffer[3]); 1037*0c0d06caSMauro Carvalho Chehab else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT) 1038*0c0d06caSMauro Carvalho Chehab sprintf(term->name, "Media %u", buffer[3]); 1039*0c0d06caSMauro Carvalho Chehab else 1040*0c0d06caSMauro Carvalho Chehab sprintf(term->name, "Input %u", buffer[3]); 1041*0c0d06caSMauro Carvalho Chehab 1042*0c0d06caSMauro Carvalho Chehab list_add_tail(&term->list, &dev->entities); 1043*0c0d06caSMauro Carvalho Chehab break; 1044*0c0d06caSMauro Carvalho Chehab 1045*0c0d06caSMauro Carvalho Chehab case UVC_VC_OUTPUT_TERMINAL: 1046*0c0d06caSMauro Carvalho Chehab if (buflen < 9) { 1047*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 1048*0c0d06caSMauro Carvalho Chehab "interface %d OUTPUT_TERMINAL error\n", 1049*0c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 1050*0c0d06caSMauro Carvalho Chehab return -EINVAL; 1051*0c0d06caSMauro Carvalho Chehab } 1052*0c0d06caSMauro Carvalho Chehab 1053*0c0d06caSMauro Carvalho Chehab /* Make sure the terminal type MSB is not null, otherwise it 1054*0c0d06caSMauro Carvalho Chehab * could be confused with a unit. 1055*0c0d06caSMauro Carvalho Chehab */ 1056*0c0d06caSMauro Carvalho Chehab type = get_unaligned_le16(&buffer[4]); 1057*0c0d06caSMauro Carvalho Chehab if ((type & 0xff00) == 0) { 1058*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 1059*0c0d06caSMauro Carvalho Chehab "interface %d OUTPUT_TERMINAL %d has invalid " 1060*0c0d06caSMauro Carvalho Chehab "type 0x%04x, skipping\n", udev->devnum, 1061*0c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, buffer[3], type); 1062*0c0d06caSMauro Carvalho Chehab return 0; 1063*0c0d06caSMauro Carvalho Chehab } 1064*0c0d06caSMauro Carvalho Chehab 1065*0c0d06caSMauro Carvalho Chehab term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3], 1066*0c0d06caSMauro Carvalho Chehab 1, 0); 1067*0c0d06caSMauro Carvalho Chehab if (term == NULL) 1068*0c0d06caSMauro Carvalho Chehab return -ENOMEM; 1069*0c0d06caSMauro Carvalho Chehab 1070*0c0d06caSMauro Carvalho Chehab memcpy(term->baSourceID, &buffer[7], 1); 1071*0c0d06caSMauro Carvalho Chehab 1072*0c0d06caSMauro Carvalho Chehab if (buffer[8] != 0) 1073*0c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[8], term->name, 1074*0c0d06caSMauro Carvalho Chehab sizeof term->name); 1075*0c0d06caSMauro Carvalho Chehab else 1076*0c0d06caSMauro Carvalho Chehab sprintf(term->name, "Output %u", buffer[3]); 1077*0c0d06caSMauro Carvalho Chehab 1078*0c0d06caSMauro Carvalho Chehab list_add_tail(&term->list, &dev->entities); 1079*0c0d06caSMauro Carvalho Chehab break; 1080*0c0d06caSMauro Carvalho Chehab 1081*0c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 1082*0c0d06caSMauro Carvalho Chehab p = buflen >= 5 ? buffer[4] : 0; 1083*0c0d06caSMauro Carvalho Chehab 1084*0c0d06caSMauro Carvalho Chehab if (buflen < 5 || buflen < 6 + p) { 1085*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 1086*0c0d06caSMauro Carvalho Chehab "interface %d SELECTOR_UNIT error\n", 1087*0c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 1088*0c0d06caSMauro Carvalho Chehab return -EINVAL; 1089*0c0d06caSMauro Carvalho Chehab } 1090*0c0d06caSMauro Carvalho Chehab 1091*0c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0); 1092*0c0d06caSMauro Carvalho Chehab if (unit == NULL) 1093*0c0d06caSMauro Carvalho Chehab return -ENOMEM; 1094*0c0d06caSMauro Carvalho Chehab 1095*0c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[5], p); 1096*0c0d06caSMauro Carvalho Chehab 1097*0c0d06caSMauro Carvalho Chehab if (buffer[5+p] != 0) 1098*0c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[5+p], unit->name, 1099*0c0d06caSMauro Carvalho Chehab sizeof unit->name); 1100*0c0d06caSMauro Carvalho Chehab else 1101*0c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Selector %u", buffer[3]); 1102*0c0d06caSMauro Carvalho Chehab 1103*0c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 1104*0c0d06caSMauro Carvalho Chehab break; 1105*0c0d06caSMauro Carvalho Chehab 1106*0c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 1107*0c0d06caSMauro Carvalho Chehab n = buflen >= 8 ? buffer[7] : 0; 1108*0c0d06caSMauro Carvalho Chehab p = dev->uvc_version >= 0x0110 ? 10 : 9; 1109*0c0d06caSMauro Carvalho Chehab 1110*0c0d06caSMauro Carvalho Chehab if (buflen < p + n) { 1111*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 1112*0c0d06caSMauro Carvalho Chehab "interface %d PROCESSING_UNIT error\n", 1113*0c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 1114*0c0d06caSMauro Carvalho Chehab return -EINVAL; 1115*0c0d06caSMauro Carvalho Chehab } 1116*0c0d06caSMauro Carvalho Chehab 1117*0c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n); 1118*0c0d06caSMauro Carvalho Chehab if (unit == NULL) 1119*0c0d06caSMauro Carvalho Chehab return -ENOMEM; 1120*0c0d06caSMauro Carvalho Chehab 1121*0c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[4], 1); 1122*0c0d06caSMauro Carvalho Chehab unit->processing.wMaxMultiplier = 1123*0c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[5]); 1124*0c0d06caSMauro Carvalho Chehab unit->processing.bControlSize = buffer[7]; 1125*0c0d06caSMauro Carvalho Chehab unit->processing.bmControls = (__u8 *)unit + sizeof *unit; 1126*0c0d06caSMauro Carvalho Chehab memcpy(unit->processing.bmControls, &buffer[8], n); 1127*0c0d06caSMauro Carvalho Chehab if (dev->uvc_version >= 0x0110) 1128*0c0d06caSMauro Carvalho Chehab unit->processing.bmVideoStandards = buffer[9+n]; 1129*0c0d06caSMauro Carvalho Chehab 1130*0c0d06caSMauro Carvalho Chehab if (buffer[8+n] != 0) 1131*0c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[8+n], unit->name, 1132*0c0d06caSMauro Carvalho Chehab sizeof unit->name); 1133*0c0d06caSMauro Carvalho Chehab else 1134*0c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Processing %u", buffer[3]); 1135*0c0d06caSMauro Carvalho Chehab 1136*0c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 1137*0c0d06caSMauro Carvalho Chehab break; 1138*0c0d06caSMauro Carvalho Chehab 1139*0c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 1140*0c0d06caSMauro Carvalho Chehab p = buflen >= 22 ? buffer[21] : 0; 1141*0c0d06caSMauro Carvalho Chehab n = buflen >= 24 + p ? buffer[22+p] : 0; 1142*0c0d06caSMauro Carvalho Chehab 1143*0c0d06caSMauro Carvalho Chehab if (buflen < 24 + p + n) { 1144*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 1145*0c0d06caSMauro Carvalho Chehab "interface %d EXTENSION_UNIT error\n", 1146*0c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 1147*0c0d06caSMauro Carvalho Chehab return -EINVAL; 1148*0c0d06caSMauro Carvalho Chehab } 1149*0c0d06caSMauro Carvalho Chehab 1150*0c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n); 1151*0c0d06caSMauro Carvalho Chehab if (unit == NULL) 1152*0c0d06caSMauro Carvalho Chehab return -ENOMEM; 1153*0c0d06caSMauro Carvalho Chehab 1154*0c0d06caSMauro Carvalho Chehab memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); 1155*0c0d06caSMauro Carvalho Chehab unit->extension.bNumControls = buffer[20]; 1156*0c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[22], p); 1157*0c0d06caSMauro Carvalho Chehab unit->extension.bControlSize = buffer[22+p]; 1158*0c0d06caSMauro Carvalho Chehab unit->extension.bmControls = (__u8 *)unit + sizeof *unit; 1159*0c0d06caSMauro Carvalho Chehab memcpy(unit->extension.bmControls, &buffer[23+p], n); 1160*0c0d06caSMauro Carvalho Chehab 1161*0c0d06caSMauro Carvalho Chehab if (buffer[23+p+n] != 0) 1162*0c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[23+p+n], unit->name, 1163*0c0d06caSMauro Carvalho Chehab sizeof unit->name); 1164*0c0d06caSMauro Carvalho Chehab else 1165*0c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Extension %u", buffer[3]); 1166*0c0d06caSMauro Carvalho Chehab 1167*0c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 1168*0c0d06caSMauro Carvalho Chehab break; 1169*0c0d06caSMauro Carvalho Chehab 1170*0c0d06caSMauro Carvalho Chehab default: 1171*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE " 1172*0c0d06caSMauro Carvalho Chehab "descriptor (%u)\n", buffer[2]); 1173*0c0d06caSMauro Carvalho Chehab break; 1174*0c0d06caSMauro Carvalho Chehab } 1175*0c0d06caSMauro Carvalho Chehab 1176*0c0d06caSMauro Carvalho Chehab return 0; 1177*0c0d06caSMauro Carvalho Chehab } 1178*0c0d06caSMauro Carvalho Chehab 1179*0c0d06caSMauro Carvalho Chehab static int uvc_parse_control(struct uvc_device *dev) 1180*0c0d06caSMauro Carvalho Chehab { 1181*0c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 1182*0c0d06caSMauro Carvalho Chehab unsigned char *buffer = alts->extra; 1183*0c0d06caSMauro Carvalho Chehab int buflen = alts->extralen; 1184*0c0d06caSMauro Carvalho Chehab int ret; 1185*0c0d06caSMauro Carvalho Chehab 1186*0c0d06caSMauro Carvalho Chehab /* Parse the default alternate setting only, as the UVC specification 1187*0c0d06caSMauro Carvalho Chehab * defines a single alternate setting, the default alternate setting 1188*0c0d06caSMauro Carvalho Chehab * zero. 1189*0c0d06caSMauro Carvalho Chehab */ 1190*0c0d06caSMauro Carvalho Chehab 1191*0c0d06caSMauro Carvalho Chehab while (buflen > 2) { 1192*0c0d06caSMauro Carvalho Chehab if (uvc_parse_vendor_control(dev, buffer, buflen) || 1193*0c0d06caSMauro Carvalho Chehab buffer[1] != USB_DT_CS_INTERFACE) 1194*0c0d06caSMauro Carvalho Chehab goto next_descriptor; 1195*0c0d06caSMauro Carvalho Chehab 1196*0c0d06caSMauro Carvalho Chehab if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0) 1197*0c0d06caSMauro Carvalho Chehab return ret; 1198*0c0d06caSMauro Carvalho Chehab 1199*0c0d06caSMauro Carvalho Chehab next_descriptor: 1200*0c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 1201*0c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 1202*0c0d06caSMauro Carvalho Chehab } 1203*0c0d06caSMauro Carvalho Chehab 1204*0c0d06caSMauro Carvalho Chehab /* Check if the optional status endpoint is present. Built-in iSight 1205*0c0d06caSMauro Carvalho Chehab * webcams have an interrupt endpoint but spit proprietary data that 1206*0c0d06caSMauro Carvalho Chehab * don't conform to the UVC status endpoint messages. Don't try to 1207*0c0d06caSMauro Carvalho Chehab * handle the interrupt endpoint for those cameras. 1208*0c0d06caSMauro Carvalho Chehab */ 1209*0c0d06caSMauro Carvalho Chehab if (alts->desc.bNumEndpoints == 1 && 1210*0c0d06caSMauro Carvalho Chehab !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) { 1211*0c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep = &alts->endpoint[0]; 1212*0c0d06caSMauro Carvalho Chehab struct usb_endpoint_descriptor *desc = &ep->desc; 1213*0c0d06caSMauro Carvalho Chehab 1214*0c0d06caSMauro Carvalho Chehab if (usb_endpoint_is_int_in(desc) && 1215*0c0d06caSMauro Carvalho Chehab le16_to_cpu(desc->wMaxPacketSize) >= 8 && 1216*0c0d06caSMauro Carvalho Chehab desc->bInterval != 0) { 1217*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint " 1218*0c0d06caSMauro Carvalho Chehab "(addr %02x).\n", desc->bEndpointAddress); 1219*0c0d06caSMauro Carvalho Chehab dev->int_ep = ep; 1220*0c0d06caSMauro Carvalho Chehab } 1221*0c0d06caSMauro Carvalho Chehab } 1222*0c0d06caSMauro Carvalho Chehab 1223*0c0d06caSMauro Carvalho Chehab return 0; 1224*0c0d06caSMauro Carvalho Chehab } 1225*0c0d06caSMauro Carvalho Chehab 1226*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 1227*0c0d06caSMauro Carvalho Chehab * UVC device scan 1228*0c0d06caSMauro Carvalho Chehab */ 1229*0c0d06caSMauro Carvalho Chehab 1230*0c0d06caSMauro Carvalho Chehab /* 1231*0c0d06caSMauro Carvalho Chehab * Scan the UVC descriptors to locate a chain starting at an Output Terminal 1232*0c0d06caSMauro Carvalho Chehab * and containing the following units: 1233*0c0d06caSMauro Carvalho Chehab * 1234*0c0d06caSMauro Carvalho Chehab * - one or more Output Terminals (USB Streaming or Display) 1235*0c0d06caSMauro Carvalho Chehab * - zero or one Processing Unit 1236*0c0d06caSMauro Carvalho Chehab * - zero, one or more single-input Selector Units 1237*0c0d06caSMauro Carvalho Chehab * - zero or one multiple-input Selector Units, provided all inputs are 1238*0c0d06caSMauro Carvalho Chehab * connected to input terminals 1239*0c0d06caSMauro Carvalho Chehab * - zero, one or mode single-input Extension Units 1240*0c0d06caSMauro Carvalho Chehab * - one or more Input Terminals (Camera, External or USB Streaming) 1241*0c0d06caSMauro Carvalho Chehab * 1242*0c0d06caSMauro Carvalho Chehab * The terminal and units must match on of the following structures: 1243*0c0d06caSMauro Carvalho Chehab * 1244*0c0d06caSMauro Carvalho Chehab * ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0) 1245*0c0d06caSMauro Carvalho Chehab * ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ... 1246*0c0d06caSMauro Carvalho Chehab * ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n) 1247*0c0d06caSMauro Carvalho Chehab * 1248*0c0d06caSMauro Carvalho Chehab * +---------+ +---------+ -> OTT_*(0) 1249*0c0d06caSMauro Carvalho Chehab * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ... 1250*0c0d06caSMauro Carvalho Chehab * +---------+ +---------+ -> OTT_*(n) 1251*0c0d06caSMauro Carvalho Chehab * 1252*0c0d06caSMauro Carvalho Chehab * The Processing Unit and Extension Units can be in any order. Additional 1253*0c0d06caSMauro Carvalho Chehab * Extension Units connected to the main chain as single-unit branches are 1254*0c0d06caSMauro Carvalho Chehab * also supported. Single-input Selector Units are ignored. 1255*0c0d06caSMauro Carvalho Chehab */ 1256*0c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_entity(struct uvc_video_chain *chain, 1257*0c0d06caSMauro Carvalho Chehab struct uvc_entity *entity) 1258*0c0d06caSMauro Carvalho Chehab { 1259*0c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(entity)) { 1260*0c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 1261*0c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 1262*0c0d06caSMauro Carvalho Chehab printk(" <- XU %d", entity->id); 1263*0c0d06caSMauro Carvalho Chehab 1264*0c0d06caSMauro Carvalho Chehab if (entity->bNrInPins != 1) { 1265*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more " 1266*0c0d06caSMauro Carvalho Chehab "than 1 input pin.\n", entity->id); 1267*0c0d06caSMauro Carvalho Chehab return -1; 1268*0c0d06caSMauro Carvalho Chehab } 1269*0c0d06caSMauro Carvalho Chehab 1270*0c0d06caSMauro Carvalho Chehab break; 1271*0c0d06caSMauro Carvalho Chehab 1272*0c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 1273*0c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 1274*0c0d06caSMauro Carvalho Chehab printk(" <- PU %d", entity->id); 1275*0c0d06caSMauro Carvalho Chehab 1276*0c0d06caSMauro Carvalho Chehab if (chain->processing != NULL) { 1277*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found multiple " 1278*0c0d06caSMauro Carvalho Chehab "Processing Units in chain.\n"); 1279*0c0d06caSMauro Carvalho Chehab return -1; 1280*0c0d06caSMauro Carvalho Chehab } 1281*0c0d06caSMauro Carvalho Chehab 1282*0c0d06caSMauro Carvalho Chehab chain->processing = entity; 1283*0c0d06caSMauro Carvalho Chehab break; 1284*0c0d06caSMauro Carvalho Chehab 1285*0c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 1286*0c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 1287*0c0d06caSMauro Carvalho Chehab printk(" <- SU %d", entity->id); 1288*0c0d06caSMauro Carvalho Chehab 1289*0c0d06caSMauro Carvalho Chehab /* Single-input selector units are ignored. */ 1290*0c0d06caSMauro Carvalho Chehab if (entity->bNrInPins == 1) 1291*0c0d06caSMauro Carvalho Chehab break; 1292*0c0d06caSMauro Carvalho Chehab 1293*0c0d06caSMauro Carvalho Chehab if (chain->selector != NULL) { 1294*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector " 1295*0c0d06caSMauro Carvalho Chehab "Units in chain.\n"); 1296*0c0d06caSMauro Carvalho Chehab return -1; 1297*0c0d06caSMauro Carvalho Chehab } 1298*0c0d06caSMauro Carvalho Chehab 1299*0c0d06caSMauro Carvalho Chehab chain->selector = entity; 1300*0c0d06caSMauro Carvalho Chehab break; 1301*0c0d06caSMauro Carvalho Chehab 1302*0c0d06caSMauro Carvalho Chehab case UVC_ITT_VENDOR_SPECIFIC: 1303*0c0d06caSMauro Carvalho Chehab case UVC_ITT_CAMERA: 1304*0c0d06caSMauro Carvalho Chehab case UVC_ITT_MEDIA_TRANSPORT_INPUT: 1305*0c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 1306*0c0d06caSMauro Carvalho Chehab printk(" <- IT %d\n", entity->id); 1307*0c0d06caSMauro Carvalho Chehab 1308*0c0d06caSMauro Carvalho Chehab break; 1309*0c0d06caSMauro Carvalho Chehab 1310*0c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 1311*0c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 1312*0c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 1313*0c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 1314*0c0d06caSMauro Carvalho Chehab printk(" OT %d", entity->id); 1315*0c0d06caSMauro Carvalho Chehab 1316*0c0d06caSMauro Carvalho Chehab break; 1317*0c0d06caSMauro Carvalho Chehab 1318*0c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 1319*0c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_IS_ITERM(entity)) { 1320*0c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 1321*0c0d06caSMauro Carvalho Chehab printk(" <- IT %d\n", entity->id); 1322*0c0d06caSMauro Carvalho Chehab } else { 1323*0c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 1324*0c0d06caSMauro Carvalho Chehab printk(" OT %d", entity->id); 1325*0c0d06caSMauro Carvalho Chehab } 1326*0c0d06caSMauro Carvalho Chehab 1327*0c0d06caSMauro Carvalho Chehab break; 1328*0c0d06caSMauro Carvalho Chehab 1329*0c0d06caSMauro Carvalho Chehab default: 1330*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type " 1331*0c0d06caSMauro Carvalho Chehab "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity)); 1332*0c0d06caSMauro Carvalho Chehab return -1; 1333*0c0d06caSMauro Carvalho Chehab } 1334*0c0d06caSMauro Carvalho Chehab 1335*0c0d06caSMauro Carvalho Chehab list_add_tail(&entity->chain, &chain->entities); 1336*0c0d06caSMauro Carvalho Chehab return 0; 1337*0c0d06caSMauro Carvalho Chehab } 1338*0c0d06caSMauro Carvalho Chehab 1339*0c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_forward(struct uvc_video_chain *chain, 1340*0c0d06caSMauro Carvalho Chehab struct uvc_entity *entity, struct uvc_entity *prev) 1341*0c0d06caSMauro Carvalho Chehab { 1342*0c0d06caSMauro Carvalho Chehab struct uvc_entity *forward; 1343*0c0d06caSMauro Carvalho Chehab int found; 1344*0c0d06caSMauro Carvalho Chehab 1345*0c0d06caSMauro Carvalho Chehab /* Forward scan */ 1346*0c0d06caSMauro Carvalho Chehab forward = NULL; 1347*0c0d06caSMauro Carvalho Chehab found = 0; 1348*0c0d06caSMauro Carvalho Chehab 1349*0c0d06caSMauro Carvalho Chehab while (1) { 1350*0c0d06caSMauro Carvalho Chehab forward = uvc_entity_by_reference(chain->dev, entity->id, 1351*0c0d06caSMauro Carvalho Chehab forward); 1352*0c0d06caSMauro Carvalho Chehab if (forward == NULL) 1353*0c0d06caSMauro Carvalho Chehab break; 1354*0c0d06caSMauro Carvalho Chehab if (forward == prev) 1355*0c0d06caSMauro Carvalho Chehab continue; 1356*0c0d06caSMauro Carvalho Chehab 1357*0c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(forward)) { 1358*0c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 1359*0c0d06caSMauro Carvalho Chehab if (forward->bNrInPins != 1) { 1360*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Extension unit %d " 1361*0c0d06caSMauro Carvalho Chehab "has more than 1 input pin.\n", 1362*0c0d06caSMauro Carvalho Chehab entity->id); 1363*0c0d06caSMauro Carvalho Chehab return -EINVAL; 1364*0c0d06caSMauro Carvalho Chehab } 1365*0c0d06caSMauro Carvalho Chehab 1366*0c0d06caSMauro Carvalho Chehab list_add_tail(&forward->chain, &chain->entities); 1367*0c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) { 1368*0c0d06caSMauro Carvalho Chehab if (!found) 1369*0c0d06caSMauro Carvalho Chehab printk(" (->"); 1370*0c0d06caSMauro Carvalho Chehab 1371*0c0d06caSMauro Carvalho Chehab printk(" XU %d", forward->id); 1372*0c0d06caSMauro Carvalho Chehab found = 1; 1373*0c0d06caSMauro Carvalho Chehab } 1374*0c0d06caSMauro Carvalho Chehab break; 1375*0c0d06caSMauro Carvalho Chehab 1376*0c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 1377*0c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 1378*0c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 1379*0c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 1380*0c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_IS_ITERM(forward)) { 1381*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Unsupported input " 1382*0c0d06caSMauro Carvalho Chehab "terminal %u.\n", forward->id); 1383*0c0d06caSMauro Carvalho Chehab return -EINVAL; 1384*0c0d06caSMauro Carvalho Chehab } 1385*0c0d06caSMauro Carvalho Chehab 1386*0c0d06caSMauro Carvalho Chehab list_add_tail(&forward->chain, &chain->entities); 1387*0c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) { 1388*0c0d06caSMauro Carvalho Chehab if (!found) 1389*0c0d06caSMauro Carvalho Chehab printk(" (->"); 1390*0c0d06caSMauro Carvalho Chehab 1391*0c0d06caSMauro Carvalho Chehab printk(" OT %d", forward->id); 1392*0c0d06caSMauro Carvalho Chehab found = 1; 1393*0c0d06caSMauro Carvalho Chehab } 1394*0c0d06caSMauro Carvalho Chehab break; 1395*0c0d06caSMauro Carvalho Chehab } 1396*0c0d06caSMauro Carvalho Chehab } 1397*0c0d06caSMauro Carvalho Chehab if (found) 1398*0c0d06caSMauro Carvalho Chehab printk(")"); 1399*0c0d06caSMauro Carvalho Chehab 1400*0c0d06caSMauro Carvalho Chehab return 0; 1401*0c0d06caSMauro Carvalho Chehab } 1402*0c0d06caSMauro Carvalho Chehab 1403*0c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_backward(struct uvc_video_chain *chain, 1404*0c0d06caSMauro Carvalho Chehab struct uvc_entity **_entity) 1405*0c0d06caSMauro Carvalho Chehab { 1406*0c0d06caSMauro Carvalho Chehab struct uvc_entity *entity = *_entity; 1407*0c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 1408*0c0d06caSMauro Carvalho Chehab int id = -EINVAL, i; 1409*0c0d06caSMauro Carvalho Chehab 1410*0c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(entity)) { 1411*0c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 1412*0c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 1413*0c0d06caSMauro Carvalho Chehab id = entity->baSourceID[0]; 1414*0c0d06caSMauro Carvalho Chehab break; 1415*0c0d06caSMauro Carvalho Chehab 1416*0c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 1417*0c0d06caSMauro Carvalho Chehab /* Single-input selector units are ignored. */ 1418*0c0d06caSMauro Carvalho Chehab if (entity->bNrInPins == 1) { 1419*0c0d06caSMauro Carvalho Chehab id = entity->baSourceID[0]; 1420*0c0d06caSMauro Carvalho Chehab break; 1421*0c0d06caSMauro Carvalho Chehab } 1422*0c0d06caSMauro Carvalho Chehab 1423*0c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 1424*0c0d06caSMauro Carvalho Chehab printk(" <- IT"); 1425*0c0d06caSMauro Carvalho Chehab 1426*0c0d06caSMauro Carvalho Chehab chain->selector = entity; 1427*0c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->bNrInPins; ++i) { 1428*0c0d06caSMauro Carvalho Chehab id = entity->baSourceID[i]; 1429*0c0d06caSMauro Carvalho Chehab term = uvc_entity_by_id(chain->dev, id); 1430*0c0d06caSMauro Carvalho Chehab if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { 1431*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Selector unit %d " 1432*0c0d06caSMauro Carvalho Chehab "input %d isn't connected to an " 1433*0c0d06caSMauro Carvalho Chehab "input terminal\n", entity->id, i); 1434*0c0d06caSMauro Carvalho Chehab return -1; 1435*0c0d06caSMauro Carvalho Chehab } 1436*0c0d06caSMauro Carvalho Chehab 1437*0c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 1438*0c0d06caSMauro Carvalho Chehab printk(" %d", term->id); 1439*0c0d06caSMauro Carvalho Chehab 1440*0c0d06caSMauro Carvalho Chehab list_add_tail(&term->chain, &chain->entities); 1441*0c0d06caSMauro Carvalho Chehab uvc_scan_chain_forward(chain, term, entity); 1442*0c0d06caSMauro Carvalho Chehab } 1443*0c0d06caSMauro Carvalho Chehab 1444*0c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 1445*0c0d06caSMauro Carvalho Chehab printk("\n"); 1446*0c0d06caSMauro Carvalho Chehab 1447*0c0d06caSMauro Carvalho Chehab id = 0; 1448*0c0d06caSMauro Carvalho Chehab break; 1449*0c0d06caSMauro Carvalho Chehab 1450*0c0d06caSMauro Carvalho Chehab case UVC_ITT_VENDOR_SPECIFIC: 1451*0c0d06caSMauro Carvalho Chehab case UVC_ITT_CAMERA: 1452*0c0d06caSMauro Carvalho Chehab case UVC_ITT_MEDIA_TRANSPORT_INPUT: 1453*0c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 1454*0c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 1455*0c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 1456*0c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 1457*0c0d06caSMauro Carvalho Chehab id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0; 1458*0c0d06caSMauro Carvalho Chehab break; 1459*0c0d06caSMauro Carvalho Chehab } 1460*0c0d06caSMauro Carvalho Chehab 1461*0c0d06caSMauro Carvalho Chehab if (id <= 0) { 1462*0c0d06caSMauro Carvalho Chehab *_entity = NULL; 1463*0c0d06caSMauro Carvalho Chehab return id; 1464*0c0d06caSMauro Carvalho Chehab } 1465*0c0d06caSMauro Carvalho Chehab 1466*0c0d06caSMauro Carvalho Chehab entity = uvc_entity_by_id(chain->dev, id); 1467*0c0d06caSMauro Carvalho Chehab if (entity == NULL) { 1468*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found reference to " 1469*0c0d06caSMauro Carvalho Chehab "unknown entity %d.\n", id); 1470*0c0d06caSMauro Carvalho Chehab return -EINVAL; 1471*0c0d06caSMauro Carvalho Chehab } 1472*0c0d06caSMauro Carvalho Chehab 1473*0c0d06caSMauro Carvalho Chehab *_entity = entity; 1474*0c0d06caSMauro Carvalho Chehab return 0; 1475*0c0d06caSMauro Carvalho Chehab } 1476*0c0d06caSMauro Carvalho Chehab 1477*0c0d06caSMauro Carvalho Chehab static int uvc_scan_chain(struct uvc_video_chain *chain, 1478*0c0d06caSMauro Carvalho Chehab struct uvc_entity *term) 1479*0c0d06caSMauro Carvalho Chehab { 1480*0c0d06caSMauro Carvalho Chehab struct uvc_entity *entity, *prev; 1481*0c0d06caSMauro Carvalho Chehab 1482*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:"); 1483*0c0d06caSMauro Carvalho Chehab 1484*0c0d06caSMauro Carvalho Chehab entity = term; 1485*0c0d06caSMauro Carvalho Chehab prev = NULL; 1486*0c0d06caSMauro Carvalho Chehab 1487*0c0d06caSMauro Carvalho Chehab while (entity != NULL) { 1488*0c0d06caSMauro Carvalho Chehab /* Entity must not be part of an existing chain */ 1489*0c0d06caSMauro Carvalho Chehab if (entity->chain.next || entity->chain.prev) { 1490*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found reference to " 1491*0c0d06caSMauro Carvalho Chehab "entity %d already in chain.\n", entity->id); 1492*0c0d06caSMauro Carvalho Chehab return -EINVAL; 1493*0c0d06caSMauro Carvalho Chehab } 1494*0c0d06caSMauro Carvalho Chehab 1495*0c0d06caSMauro Carvalho Chehab /* Process entity */ 1496*0c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_entity(chain, entity) < 0) 1497*0c0d06caSMauro Carvalho Chehab return -EINVAL; 1498*0c0d06caSMauro Carvalho Chehab 1499*0c0d06caSMauro Carvalho Chehab /* Forward scan */ 1500*0c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_forward(chain, entity, prev) < 0) 1501*0c0d06caSMauro Carvalho Chehab return -EINVAL; 1502*0c0d06caSMauro Carvalho Chehab 1503*0c0d06caSMauro Carvalho Chehab /* Backward scan */ 1504*0c0d06caSMauro Carvalho Chehab prev = entity; 1505*0c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_backward(chain, &entity) < 0) 1506*0c0d06caSMauro Carvalho Chehab return -EINVAL; 1507*0c0d06caSMauro Carvalho Chehab } 1508*0c0d06caSMauro Carvalho Chehab 1509*0c0d06caSMauro Carvalho Chehab return 0; 1510*0c0d06caSMauro Carvalho Chehab } 1511*0c0d06caSMauro Carvalho Chehab 1512*0c0d06caSMauro Carvalho Chehab static unsigned int uvc_print_terms(struct list_head *terms, u16 dir, 1513*0c0d06caSMauro Carvalho Chehab char *buffer) 1514*0c0d06caSMauro Carvalho Chehab { 1515*0c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 1516*0c0d06caSMauro Carvalho Chehab unsigned int nterms = 0; 1517*0c0d06caSMauro Carvalho Chehab char *p = buffer; 1518*0c0d06caSMauro Carvalho Chehab 1519*0c0d06caSMauro Carvalho Chehab list_for_each_entry(term, terms, chain) { 1520*0c0d06caSMauro Carvalho Chehab if (!UVC_ENTITY_IS_TERM(term) || 1521*0c0d06caSMauro Carvalho Chehab UVC_TERM_DIRECTION(term) != dir) 1522*0c0d06caSMauro Carvalho Chehab continue; 1523*0c0d06caSMauro Carvalho Chehab 1524*0c0d06caSMauro Carvalho Chehab if (nterms) 1525*0c0d06caSMauro Carvalho Chehab p += sprintf(p, ","); 1526*0c0d06caSMauro Carvalho Chehab if (++nterms >= 4) { 1527*0c0d06caSMauro Carvalho Chehab p += sprintf(p, "..."); 1528*0c0d06caSMauro Carvalho Chehab break; 1529*0c0d06caSMauro Carvalho Chehab } 1530*0c0d06caSMauro Carvalho Chehab p += sprintf(p, "%u", term->id); 1531*0c0d06caSMauro Carvalho Chehab } 1532*0c0d06caSMauro Carvalho Chehab 1533*0c0d06caSMauro Carvalho Chehab return p - buffer; 1534*0c0d06caSMauro Carvalho Chehab } 1535*0c0d06caSMauro Carvalho Chehab 1536*0c0d06caSMauro Carvalho Chehab static const char *uvc_print_chain(struct uvc_video_chain *chain) 1537*0c0d06caSMauro Carvalho Chehab { 1538*0c0d06caSMauro Carvalho Chehab static char buffer[43]; 1539*0c0d06caSMauro Carvalho Chehab char *p = buffer; 1540*0c0d06caSMauro Carvalho Chehab 1541*0c0d06caSMauro Carvalho Chehab p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p); 1542*0c0d06caSMauro Carvalho Chehab p += sprintf(p, " -> "); 1543*0c0d06caSMauro Carvalho Chehab uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p); 1544*0c0d06caSMauro Carvalho Chehab 1545*0c0d06caSMauro Carvalho Chehab return buffer; 1546*0c0d06caSMauro Carvalho Chehab } 1547*0c0d06caSMauro Carvalho Chehab 1548*0c0d06caSMauro Carvalho Chehab /* 1549*0c0d06caSMauro Carvalho Chehab * Scan the device for video chains and register video devices. 1550*0c0d06caSMauro Carvalho Chehab * 1551*0c0d06caSMauro Carvalho Chehab * Chains are scanned starting at their output terminals and walked backwards. 1552*0c0d06caSMauro Carvalho Chehab */ 1553*0c0d06caSMauro Carvalho Chehab static int uvc_scan_device(struct uvc_device *dev) 1554*0c0d06caSMauro Carvalho Chehab { 1555*0c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 1556*0c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 1557*0c0d06caSMauro Carvalho Chehab 1558*0c0d06caSMauro Carvalho Chehab list_for_each_entry(term, &dev->entities, list) { 1559*0c0d06caSMauro Carvalho Chehab if (!UVC_ENTITY_IS_OTERM(term)) 1560*0c0d06caSMauro Carvalho Chehab continue; 1561*0c0d06caSMauro Carvalho Chehab 1562*0c0d06caSMauro Carvalho Chehab /* If the terminal is already included in a chain, skip it. 1563*0c0d06caSMauro Carvalho Chehab * This can happen for chains that have multiple output 1564*0c0d06caSMauro Carvalho Chehab * terminals, where all output terminals beside the first one 1565*0c0d06caSMauro Carvalho Chehab * will be inserted in the chain in forward scans. 1566*0c0d06caSMauro Carvalho Chehab */ 1567*0c0d06caSMauro Carvalho Chehab if (term->chain.next || term->chain.prev) 1568*0c0d06caSMauro Carvalho Chehab continue; 1569*0c0d06caSMauro Carvalho Chehab 1570*0c0d06caSMauro Carvalho Chehab chain = kzalloc(sizeof(*chain), GFP_KERNEL); 1571*0c0d06caSMauro Carvalho Chehab if (chain == NULL) 1572*0c0d06caSMauro Carvalho Chehab return -ENOMEM; 1573*0c0d06caSMauro Carvalho Chehab 1574*0c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&chain->entities); 1575*0c0d06caSMauro Carvalho Chehab mutex_init(&chain->ctrl_mutex); 1576*0c0d06caSMauro Carvalho Chehab chain->dev = dev; 1577*0c0d06caSMauro Carvalho Chehab 1578*0c0d06caSMauro Carvalho Chehab if (uvc_scan_chain(chain, term) < 0) { 1579*0c0d06caSMauro Carvalho Chehab kfree(chain); 1580*0c0d06caSMauro Carvalho Chehab continue; 1581*0c0d06caSMauro Carvalho Chehab } 1582*0c0d06caSMauro Carvalho Chehab 1583*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n", 1584*0c0d06caSMauro Carvalho Chehab uvc_print_chain(chain)); 1585*0c0d06caSMauro Carvalho Chehab 1586*0c0d06caSMauro Carvalho Chehab list_add_tail(&chain->list, &dev->chains); 1587*0c0d06caSMauro Carvalho Chehab } 1588*0c0d06caSMauro Carvalho Chehab 1589*0c0d06caSMauro Carvalho Chehab if (list_empty(&dev->chains)) { 1590*0c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "No valid video chain found.\n"); 1591*0c0d06caSMauro Carvalho Chehab return -1; 1592*0c0d06caSMauro Carvalho Chehab } 1593*0c0d06caSMauro Carvalho Chehab 1594*0c0d06caSMauro Carvalho Chehab return 0; 1595*0c0d06caSMauro Carvalho Chehab } 1596*0c0d06caSMauro Carvalho Chehab 1597*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 1598*0c0d06caSMauro Carvalho Chehab * Video device registration and unregistration 1599*0c0d06caSMauro Carvalho Chehab */ 1600*0c0d06caSMauro Carvalho Chehab 1601*0c0d06caSMauro Carvalho Chehab /* 1602*0c0d06caSMauro Carvalho Chehab * Delete the UVC device. 1603*0c0d06caSMauro Carvalho Chehab * 1604*0c0d06caSMauro Carvalho Chehab * Called by the kernel when the last reference to the uvc_device structure 1605*0c0d06caSMauro Carvalho Chehab * is released. 1606*0c0d06caSMauro Carvalho Chehab * 1607*0c0d06caSMauro Carvalho Chehab * As this function is called after or during disconnect(), all URBs have 1608*0c0d06caSMauro Carvalho Chehab * already been canceled by the USB core. There is no need to kill the 1609*0c0d06caSMauro Carvalho Chehab * interrupt URB manually. 1610*0c0d06caSMauro Carvalho Chehab */ 1611*0c0d06caSMauro Carvalho Chehab static void uvc_delete(struct uvc_device *dev) 1612*0c0d06caSMauro Carvalho Chehab { 1613*0c0d06caSMauro Carvalho Chehab struct list_head *p, *n; 1614*0c0d06caSMauro Carvalho Chehab 1615*0c0d06caSMauro Carvalho Chehab usb_put_intf(dev->intf); 1616*0c0d06caSMauro Carvalho Chehab usb_put_dev(dev->udev); 1617*0c0d06caSMauro Carvalho Chehab 1618*0c0d06caSMauro Carvalho Chehab uvc_status_cleanup(dev); 1619*0c0d06caSMauro Carvalho Chehab uvc_ctrl_cleanup_device(dev); 1620*0c0d06caSMauro Carvalho Chehab 1621*0c0d06caSMauro Carvalho Chehab if (dev->vdev.dev) 1622*0c0d06caSMauro Carvalho Chehab v4l2_device_unregister(&dev->vdev); 1623*0c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 1624*0c0d06caSMauro Carvalho Chehab if (media_devnode_is_registered(&dev->mdev.devnode)) 1625*0c0d06caSMauro Carvalho Chehab media_device_unregister(&dev->mdev); 1626*0c0d06caSMauro Carvalho Chehab #endif 1627*0c0d06caSMauro Carvalho Chehab 1628*0c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->chains) { 1629*0c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 1630*0c0d06caSMauro Carvalho Chehab chain = list_entry(p, struct uvc_video_chain, list); 1631*0c0d06caSMauro Carvalho Chehab kfree(chain); 1632*0c0d06caSMauro Carvalho Chehab } 1633*0c0d06caSMauro Carvalho Chehab 1634*0c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->entities) { 1635*0c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 1636*0c0d06caSMauro Carvalho Chehab entity = list_entry(p, struct uvc_entity, list); 1637*0c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 1638*0c0d06caSMauro Carvalho Chehab uvc_mc_cleanup_entity(entity); 1639*0c0d06caSMauro Carvalho Chehab #endif 1640*0c0d06caSMauro Carvalho Chehab if (entity->vdev) { 1641*0c0d06caSMauro Carvalho Chehab video_device_release(entity->vdev); 1642*0c0d06caSMauro Carvalho Chehab entity->vdev = NULL; 1643*0c0d06caSMauro Carvalho Chehab } 1644*0c0d06caSMauro Carvalho Chehab kfree(entity); 1645*0c0d06caSMauro Carvalho Chehab } 1646*0c0d06caSMauro Carvalho Chehab 1647*0c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->streams) { 1648*0c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming; 1649*0c0d06caSMauro Carvalho Chehab streaming = list_entry(p, struct uvc_streaming, list); 1650*0c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, 1651*0c0d06caSMauro Carvalho Chehab streaming->intf); 1652*0c0d06caSMauro Carvalho Chehab usb_put_intf(streaming->intf); 1653*0c0d06caSMauro Carvalho Chehab kfree(streaming->format); 1654*0c0d06caSMauro Carvalho Chehab kfree(streaming->header.bmaControls); 1655*0c0d06caSMauro Carvalho Chehab kfree(streaming); 1656*0c0d06caSMauro Carvalho Chehab } 1657*0c0d06caSMauro Carvalho Chehab 1658*0c0d06caSMauro Carvalho Chehab kfree(dev); 1659*0c0d06caSMauro Carvalho Chehab } 1660*0c0d06caSMauro Carvalho Chehab 1661*0c0d06caSMauro Carvalho Chehab static void uvc_release(struct video_device *vdev) 1662*0c0d06caSMauro Carvalho Chehab { 1663*0c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream = video_get_drvdata(vdev); 1664*0c0d06caSMauro Carvalho Chehab struct uvc_device *dev = stream->dev; 1665*0c0d06caSMauro Carvalho Chehab 1666*0c0d06caSMauro Carvalho Chehab /* Decrement the registered streams count and delete the device when it 1667*0c0d06caSMauro Carvalho Chehab * reaches zero. 1668*0c0d06caSMauro Carvalho Chehab */ 1669*0c0d06caSMauro Carvalho Chehab if (atomic_dec_and_test(&dev->nstreams)) 1670*0c0d06caSMauro Carvalho Chehab uvc_delete(dev); 1671*0c0d06caSMauro Carvalho Chehab } 1672*0c0d06caSMauro Carvalho Chehab 1673*0c0d06caSMauro Carvalho Chehab /* 1674*0c0d06caSMauro Carvalho Chehab * Unregister the video devices. 1675*0c0d06caSMauro Carvalho Chehab */ 1676*0c0d06caSMauro Carvalho Chehab static void uvc_unregister_video(struct uvc_device *dev) 1677*0c0d06caSMauro Carvalho Chehab { 1678*0c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 1679*0c0d06caSMauro Carvalho Chehab 1680*0c0d06caSMauro Carvalho Chehab /* Unregistering all video devices might result in uvc_delete() being 1681*0c0d06caSMauro Carvalho Chehab * called from inside the loop if there's no open file handle. To avoid 1682*0c0d06caSMauro Carvalho Chehab * that, increment the stream count before iterating over the streams 1683*0c0d06caSMauro Carvalho Chehab * and decrement it when done. 1684*0c0d06caSMauro Carvalho Chehab */ 1685*0c0d06caSMauro Carvalho Chehab atomic_inc(&dev->nstreams); 1686*0c0d06caSMauro Carvalho Chehab 1687*0c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 1688*0c0d06caSMauro Carvalho Chehab if (stream->vdev == NULL) 1689*0c0d06caSMauro Carvalho Chehab continue; 1690*0c0d06caSMauro Carvalho Chehab 1691*0c0d06caSMauro Carvalho Chehab video_unregister_device(stream->vdev); 1692*0c0d06caSMauro Carvalho Chehab stream->vdev = NULL; 1693*0c0d06caSMauro Carvalho Chehab 1694*0c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup_stream(stream); 1695*0c0d06caSMauro Carvalho Chehab } 1696*0c0d06caSMauro Carvalho Chehab 1697*0c0d06caSMauro Carvalho Chehab /* Decrement the stream count and call uvc_delete explicitly if there 1698*0c0d06caSMauro Carvalho Chehab * are no stream left. 1699*0c0d06caSMauro Carvalho Chehab */ 1700*0c0d06caSMauro Carvalho Chehab if (atomic_dec_and_test(&dev->nstreams)) 1701*0c0d06caSMauro Carvalho Chehab uvc_delete(dev); 1702*0c0d06caSMauro Carvalho Chehab } 1703*0c0d06caSMauro Carvalho Chehab 1704*0c0d06caSMauro Carvalho Chehab static int uvc_register_video(struct uvc_device *dev, 1705*0c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream) 1706*0c0d06caSMauro Carvalho Chehab { 1707*0c0d06caSMauro Carvalho Chehab struct video_device *vdev; 1708*0c0d06caSMauro Carvalho Chehab int ret; 1709*0c0d06caSMauro Carvalho Chehab 1710*0c0d06caSMauro Carvalho Chehab /* Initialize the streaming interface with default streaming 1711*0c0d06caSMauro Carvalho Chehab * parameters. 1712*0c0d06caSMauro Carvalho Chehab */ 1713*0c0d06caSMauro Carvalho Chehab ret = uvc_video_init(stream); 1714*0c0d06caSMauro Carvalho Chehab if (ret < 0) { 1715*0c0d06caSMauro Carvalho Chehab uvc_printk(KERN_ERR, "Failed to initialize the device " 1716*0c0d06caSMauro Carvalho Chehab "(%d).\n", ret); 1717*0c0d06caSMauro Carvalho Chehab return ret; 1718*0c0d06caSMauro Carvalho Chehab } 1719*0c0d06caSMauro Carvalho Chehab 1720*0c0d06caSMauro Carvalho Chehab uvc_debugfs_init_stream(stream); 1721*0c0d06caSMauro Carvalho Chehab 1722*0c0d06caSMauro Carvalho Chehab /* Register the device with V4L. */ 1723*0c0d06caSMauro Carvalho Chehab vdev = video_device_alloc(); 1724*0c0d06caSMauro Carvalho Chehab if (vdev == NULL) { 1725*0c0d06caSMauro Carvalho Chehab uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n", 1726*0c0d06caSMauro Carvalho Chehab ret); 1727*0c0d06caSMauro Carvalho Chehab return -ENOMEM; 1728*0c0d06caSMauro Carvalho Chehab } 1729*0c0d06caSMauro Carvalho Chehab 1730*0c0d06caSMauro Carvalho Chehab /* We already hold a reference to dev->udev. The video device will be 1731*0c0d06caSMauro Carvalho Chehab * unregistered before the reference is released, so we don't need to 1732*0c0d06caSMauro Carvalho Chehab * get another one. 1733*0c0d06caSMauro Carvalho Chehab */ 1734*0c0d06caSMauro Carvalho Chehab vdev->v4l2_dev = &dev->vdev; 1735*0c0d06caSMauro Carvalho Chehab vdev->fops = &uvc_fops; 1736*0c0d06caSMauro Carvalho Chehab vdev->release = uvc_release; 1737*0c0d06caSMauro Carvalho Chehab strlcpy(vdev->name, dev->name, sizeof vdev->name); 1738*0c0d06caSMauro Carvalho Chehab 1739*0c0d06caSMauro Carvalho Chehab /* Set the driver data before calling video_register_device, otherwise 1740*0c0d06caSMauro Carvalho Chehab * uvc_v4l2_open might race us. 1741*0c0d06caSMauro Carvalho Chehab */ 1742*0c0d06caSMauro Carvalho Chehab stream->vdev = vdev; 1743*0c0d06caSMauro Carvalho Chehab video_set_drvdata(vdev, stream); 1744*0c0d06caSMauro Carvalho Chehab 1745*0c0d06caSMauro Carvalho Chehab ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); 1746*0c0d06caSMauro Carvalho Chehab if (ret < 0) { 1747*0c0d06caSMauro Carvalho Chehab uvc_printk(KERN_ERR, "Failed to register video device (%d).\n", 1748*0c0d06caSMauro Carvalho Chehab ret); 1749*0c0d06caSMauro Carvalho Chehab stream->vdev = NULL; 1750*0c0d06caSMauro Carvalho Chehab video_device_release(vdev); 1751*0c0d06caSMauro Carvalho Chehab return ret; 1752*0c0d06caSMauro Carvalho Chehab } 1753*0c0d06caSMauro Carvalho Chehab 1754*0c0d06caSMauro Carvalho Chehab atomic_inc(&dev->nstreams); 1755*0c0d06caSMauro Carvalho Chehab return 0; 1756*0c0d06caSMauro Carvalho Chehab } 1757*0c0d06caSMauro Carvalho Chehab 1758*0c0d06caSMauro Carvalho Chehab /* 1759*0c0d06caSMauro Carvalho Chehab * Register all video devices in all chains. 1760*0c0d06caSMauro Carvalho Chehab */ 1761*0c0d06caSMauro Carvalho Chehab static int uvc_register_terms(struct uvc_device *dev, 1762*0c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain) 1763*0c0d06caSMauro Carvalho Chehab { 1764*0c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 1765*0c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 1766*0c0d06caSMauro Carvalho Chehab int ret; 1767*0c0d06caSMauro Carvalho Chehab 1768*0c0d06caSMauro Carvalho Chehab list_for_each_entry(term, &chain->entities, chain) { 1769*0c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) 1770*0c0d06caSMauro Carvalho Chehab continue; 1771*0c0d06caSMauro Carvalho Chehab 1772*0c0d06caSMauro Carvalho Chehab stream = uvc_stream_by_id(dev, term->id); 1773*0c0d06caSMauro Carvalho Chehab if (stream == NULL) { 1774*0c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "No streaming interface found " 1775*0c0d06caSMauro Carvalho Chehab "for terminal %u.", term->id); 1776*0c0d06caSMauro Carvalho Chehab continue; 1777*0c0d06caSMauro Carvalho Chehab } 1778*0c0d06caSMauro Carvalho Chehab 1779*0c0d06caSMauro Carvalho Chehab stream->chain = chain; 1780*0c0d06caSMauro Carvalho Chehab ret = uvc_register_video(dev, stream); 1781*0c0d06caSMauro Carvalho Chehab if (ret < 0) 1782*0c0d06caSMauro Carvalho Chehab return ret; 1783*0c0d06caSMauro Carvalho Chehab 1784*0c0d06caSMauro Carvalho Chehab term->vdev = stream->vdev; 1785*0c0d06caSMauro Carvalho Chehab } 1786*0c0d06caSMauro Carvalho Chehab 1787*0c0d06caSMauro Carvalho Chehab return 0; 1788*0c0d06caSMauro Carvalho Chehab } 1789*0c0d06caSMauro Carvalho Chehab 1790*0c0d06caSMauro Carvalho Chehab static int uvc_register_chains(struct uvc_device *dev) 1791*0c0d06caSMauro Carvalho Chehab { 1792*0c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 1793*0c0d06caSMauro Carvalho Chehab int ret; 1794*0c0d06caSMauro Carvalho Chehab 1795*0c0d06caSMauro Carvalho Chehab list_for_each_entry(chain, &dev->chains, list) { 1796*0c0d06caSMauro Carvalho Chehab ret = uvc_register_terms(dev, chain); 1797*0c0d06caSMauro Carvalho Chehab if (ret < 0) 1798*0c0d06caSMauro Carvalho Chehab return ret; 1799*0c0d06caSMauro Carvalho Chehab 1800*0c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 1801*0c0d06caSMauro Carvalho Chehab ret = uvc_mc_register_entities(chain); 1802*0c0d06caSMauro Carvalho Chehab if (ret < 0) { 1803*0c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "Failed to register entites " 1804*0c0d06caSMauro Carvalho Chehab "(%d).\n", ret); 1805*0c0d06caSMauro Carvalho Chehab } 1806*0c0d06caSMauro Carvalho Chehab #endif 1807*0c0d06caSMauro Carvalho Chehab } 1808*0c0d06caSMauro Carvalho Chehab 1809*0c0d06caSMauro Carvalho Chehab return 0; 1810*0c0d06caSMauro Carvalho Chehab } 1811*0c0d06caSMauro Carvalho Chehab 1812*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 1813*0c0d06caSMauro Carvalho Chehab * USB probe, disconnect, suspend and resume 1814*0c0d06caSMauro Carvalho Chehab */ 1815*0c0d06caSMauro Carvalho Chehab 1816*0c0d06caSMauro Carvalho Chehab static int uvc_probe(struct usb_interface *intf, 1817*0c0d06caSMauro Carvalho Chehab const struct usb_device_id *id) 1818*0c0d06caSMauro Carvalho Chehab { 1819*0c0d06caSMauro Carvalho Chehab struct usb_device *udev = interface_to_usbdev(intf); 1820*0c0d06caSMauro Carvalho Chehab struct uvc_device *dev; 1821*0c0d06caSMauro Carvalho Chehab int ret; 1822*0c0d06caSMauro Carvalho Chehab 1823*0c0d06caSMauro Carvalho Chehab if (id->idVendor && id->idProduct) 1824*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s " 1825*0c0d06caSMauro Carvalho Chehab "(%04x:%04x)\n", udev->devpath, id->idVendor, 1826*0c0d06caSMauro Carvalho Chehab id->idProduct); 1827*0c0d06caSMauro Carvalho Chehab else 1828*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n", 1829*0c0d06caSMauro Carvalho Chehab udev->devpath); 1830*0c0d06caSMauro Carvalho Chehab 1831*0c0d06caSMauro Carvalho Chehab /* Allocate memory for the device and initialize it. */ 1832*0c0d06caSMauro Carvalho Chehab if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL) 1833*0c0d06caSMauro Carvalho Chehab return -ENOMEM; 1834*0c0d06caSMauro Carvalho Chehab 1835*0c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->entities); 1836*0c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->chains); 1837*0c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->streams); 1838*0c0d06caSMauro Carvalho Chehab atomic_set(&dev->nstreams, 0); 1839*0c0d06caSMauro Carvalho Chehab atomic_set(&dev->users, 0); 1840*0c0d06caSMauro Carvalho Chehab atomic_set(&dev->nmappings, 0); 1841*0c0d06caSMauro Carvalho Chehab 1842*0c0d06caSMauro Carvalho Chehab dev->udev = usb_get_dev(udev); 1843*0c0d06caSMauro Carvalho Chehab dev->intf = usb_get_intf(intf); 1844*0c0d06caSMauro Carvalho Chehab dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; 1845*0c0d06caSMauro Carvalho Chehab dev->quirks = (uvc_quirks_param == -1) 1846*0c0d06caSMauro Carvalho Chehab ? id->driver_info : uvc_quirks_param; 1847*0c0d06caSMauro Carvalho Chehab 1848*0c0d06caSMauro Carvalho Chehab if (udev->product != NULL) 1849*0c0d06caSMauro Carvalho Chehab strlcpy(dev->name, udev->product, sizeof dev->name); 1850*0c0d06caSMauro Carvalho Chehab else 1851*0c0d06caSMauro Carvalho Chehab snprintf(dev->name, sizeof dev->name, 1852*0c0d06caSMauro Carvalho Chehab "UVC Camera (%04x:%04x)", 1853*0c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idVendor), 1854*0c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idProduct)); 1855*0c0d06caSMauro Carvalho Chehab 1856*0c0d06caSMauro Carvalho Chehab /* Parse the Video Class control descriptor. */ 1857*0c0d06caSMauro Carvalho Chehab if (uvc_parse_control(dev) < 0) { 1858*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC " 1859*0c0d06caSMauro Carvalho Chehab "descriptors.\n"); 1860*0c0d06caSMauro Carvalho Chehab goto error; 1861*0c0d06caSMauro Carvalho Chehab } 1862*0c0d06caSMauro Carvalho Chehab 1863*0c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n", 1864*0c0d06caSMauro Carvalho Chehab dev->uvc_version >> 8, dev->uvc_version & 0xff, 1865*0c0d06caSMauro Carvalho Chehab udev->product ? udev->product : "<unnamed>", 1866*0c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idVendor), 1867*0c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idProduct)); 1868*0c0d06caSMauro Carvalho Chehab 1869*0c0d06caSMauro Carvalho Chehab if (dev->quirks != id->driver_info) { 1870*0c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module " 1871*0c0d06caSMauro Carvalho Chehab "parameter for testing purpose.\n", dev->quirks); 1872*0c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "Please report required quirks to the " 1873*0c0d06caSMauro Carvalho Chehab "linux-uvc-devel mailing list.\n"); 1874*0c0d06caSMauro Carvalho Chehab } 1875*0c0d06caSMauro Carvalho Chehab 1876*0c0d06caSMauro Carvalho Chehab /* Register the media and V4L2 devices. */ 1877*0c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 1878*0c0d06caSMauro Carvalho Chehab dev->mdev.dev = &intf->dev; 1879*0c0d06caSMauro Carvalho Chehab strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model)); 1880*0c0d06caSMauro Carvalho Chehab if (udev->serial) 1881*0c0d06caSMauro Carvalho Chehab strlcpy(dev->mdev.serial, udev->serial, 1882*0c0d06caSMauro Carvalho Chehab sizeof(dev->mdev.serial)); 1883*0c0d06caSMauro Carvalho Chehab strcpy(dev->mdev.bus_info, udev->devpath); 1884*0c0d06caSMauro Carvalho Chehab dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); 1885*0c0d06caSMauro Carvalho Chehab dev->mdev.driver_version = LINUX_VERSION_CODE; 1886*0c0d06caSMauro Carvalho Chehab if (media_device_register(&dev->mdev) < 0) 1887*0c0d06caSMauro Carvalho Chehab goto error; 1888*0c0d06caSMauro Carvalho Chehab 1889*0c0d06caSMauro Carvalho Chehab dev->vdev.mdev = &dev->mdev; 1890*0c0d06caSMauro Carvalho Chehab #endif 1891*0c0d06caSMauro Carvalho Chehab if (v4l2_device_register(&intf->dev, &dev->vdev) < 0) 1892*0c0d06caSMauro Carvalho Chehab goto error; 1893*0c0d06caSMauro Carvalho Chehab 1894*0c0d06caSMauro Carvalho Chehab /* Initialize controls. */ 1895*0c0d06caSMauro Carvalho Chehab if (uvc_ctrl_init_device(dev) < 0) 1896*0c0d06caSMauro Carvalho Chehab goto error; 1897*0c0d06caSMauro Carvalho Chehab 1898*0c0d06caSMauro Carvalho Chehab /* Scan the device for video chains. */ 1899*0c0d06caSMauro Carvalho Chehab if (uvc_scan_device(dev) < 0) 1900*0c0d06caSMauro Carvalho Chehab goto error; 1901*0c0d06caSMauro Carvalho Chehab 1902*0c0d06caSMauro Carvalho Chehab /* Register video device nodes. */ 1903*0c0d06caSMauro Carvalho Chehab if (uvc_register_chains(dev) < 0) 1904*0c0d06caSMauro Carvalho Chehab goto error; 1905*0c0d06caSMauro Carvalho Chehab 1906*0c0d06caSMauro Carvalho Chehab /* Save our data pointer in the interface data. */ 1907*0c0d06caSMauro Carvalho Chehab usb_set_intfdata(intf, dev); 1908*0c0d06caSMauro Carvalho Chehab 1909*0c0d06caSMauro Carvalho Chehab /* Initialize the interrupt URB. */ 1910*0c0d06caSMauro Carvalho Chehab if ((ret = uvc_status_init(dev)) < 0) { 1911*0c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "Unable to initialize the status " 1912*0c0d06caSMauro Carvalho Chehab "endpoint (%d), status interrupt will not be " 1913*0c0d06caSMauro Carvalho Chehab "supported.\n", ret); 1914*0c0d06caSMauro Carvalho Chehab } 1915*0c0d06caSMauro Carvalho Chehab 1916*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n"); 1917*0c0d06caSMauro Carvalho Chehab usb_enable_autosuspend(udev); 1918*0c0d06caSMauro Carvalho Chehab return 0; 1919*0c0d06caSMauro Carvalho Chehab 1920*0c0d06caSMauro Carvalho Chehab error: 1921*0c0d06caSMauro Carvalho Chehab uvc_unregister_video(dev); 1922*0c0d06caSMauro Carvalho Chehab return -ENODEV; 1923*0c0d06caSMauro Carvalho Chehab } 1924*0c0d06caSMauro Carvalho Chehab 1925*0c0d06caSMauro Carvalho Chehab static void uvc_disconnect(struct usb_interface *intf) 1926*0c0d06caSMauro Carvalho Chehab { 1927*0c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 1928*0c0d06caSMauro Carvalho Chehab 1929*0c0d06caSMauro Carvalho Chehab /* Set the USB interface data to NULL. This can be done outside the 1930*0c0d06caSMauro Carvalho Chehab * lock, as there's no other reader. 1931*0c0d06caSMauro Carvalho Chehab */ 1932*0c0d06caSMauro Carvalho Chehab usb_set_intfdata(intf, NULL); 1933*0c0d06caSMauro Carvalho Chehab 1934*0c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 1935*0c0d06caSMauro Carvalho Chehab UVC_SC_VIDEOSTREAMING) 1936*0c0d06caSMauro Carvalho Chehab return; 1937*0c0d06caSMauro Carvalho Chehab 1938*0c0d06caSMauro Carvalho Chehab dev->state |= UVC_DEV_DISCONNECTED; 1939*0c0d06caSMauro Carvalho Chehab 1940*0c0d06caSMauro Carvalho Chehab uvc_unregister_video(dev); 1941*0c0d06caSMauro Carvalho Chehab } 1942*0c0d06caSMauro Carvalho Chehab 1943*0c0d06caSMauro Carvalho Chehab static int uvc_suspend(struct usb_interface *intf, pm_message_t message) 1944*0c0d06caSMauro Carvalho Chehab { 1945*0c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 1946*0c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 1947*0c0d06caSMauro Carvalho Chehab 1948*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n", 1949*0c0d06caSMauro Carvalho Chehab intf->cur_altsetting->desc.bInterfaceNumber); 1950*0c0d06caSMauro Carvalho Chehab 1951*0c0d06caSMauro Carvalho Chehab /* Controls are cached on the fly so they don't need to be saved. */ 1952*0c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 1953*0c0d06caSMauro Carvalho Chehab UVC_SC_VIDEOCONTROL) 1954*0c0d06caSMauro Carvalho Chehab return uvc_status_suspend(dev); 1955*0c0d06caSMauro Carvalho Chehab 1956*0c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 1957*0c0d06caSMauro Carvalho Chehab if (stream->intf == intf) 1958*0c0d06caSMauro Carvalho Chehab return uvc_video_suspend(stream); 1959*0c0d06caSMauro Carvalho Chehab } 1960*0c0d06caSMauro Carvalho Chehab 1961*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface " 1962*0c0d06caSMauro Carvalho Chehab "mismatch.\n"); 1963*0c0d06caSMauro Carvalho Chehab return -EINVAL; 1964*0c0d06caSMauro Carvalho Chehab } 1965*0c0d06caSMauro Carvalho Chehab 1966*0c0d06caSMauro Carvalho Chehab static int __uvc_resume(struct usb_interface *intf, int reset) 1967*0c0d06caSMauro Carvalho Chehab { 1968*0c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 1969*0c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 1970*0c0d06caSMauro Carvalho Chehab 1971*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n", 1972*0c0d06caSMauro Carvalho Chehab intf->cur_altsetting->desc.bInterfaceNumber); 1973*0c0d06caSMauro Carvalho Chehab 1974*0c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 1975*0c0d06caSMauro Carvalho Chehab UVC_SC_VIDEOCONTROL) { 1976*0c0d06caSMauro Carvalho Chehab if (reset) { 1977*0c0d06caSMauro Carvalho Chehab int ret = uvc_ctrl_resume_device(dev); 1978*0c0d06caSMauro Carvalho Chehab 1979*0c0d06caSMauro Carvalho Chehab if (ret < 0) 1980*0c0d06caSMauro Carvalho Chehab return ret; 1981*0c0d06caSMauro Carvalho Chehab } 1982*0c0d06caSMauro Carvalho Chehab 1983*0c0d06caSMauro Carvalho Chehab return uvc_status_resume(dev); 1984*0c0d06caSMauro Carvalho Chehab } 1985*0c0d06caSMauro Carvalho Chehab 1986*0c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 1987*0c0d06caSMauro Carvalho Chehab if (stream->intf == intf) 1988*0c0d06caSMauro Carvalho Chehab return uvc_video_resume(stream, reset); 1989*0c0d06caSMauro Carvalho Chehab } 1990*0c0d06caSMauro Carvalho Chehab 1991*0c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface " 1992*0c0d06caSMauro Carvalho Chehab "mismatch.\n"); 1993*0c0d06caSMauro Carvalho Chehab return -EINVAL; 1994*0c0d06caSMauro Carvalho Chehab } 1995*0c0d06caSMauro Carvalho Chehab 1996*0c0d06caSMauro Carvalho Chehab static int uvc_resume(struct usb_interface *intf) 1997*0c0d06caSMauro Carvalho Chehab { 1998*0c0d06caSMauro Carvalho Chehab return __uvc_resume(intf, 0); 1999*0c0d06caSMauro Carvalho Chehab } 2000*0c0d06caSMauro Carvalho Chehab 2001*0c0d06caSMauro Carvalho Chehab static int uvc_reset_resume(struct usb_interface *intf) 2002*0c0d06caSMauro Carvalho Chehab { 2003*0c0d06caSMauro Carvalho Chehab return __uvc_resume(intf, 1); 2004*0c0d06caSMauro Carvalho Chehab } 2005*0c0d06caSMauro Carvalho Chehab 2006*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 2007*0c0d06caSMauro Carvalho Chehab * Module parameters 2008*0c0d06caSMauro Carvalho Chehab */ 2009*0c0d06caSMauro Carvalho Chehab 2010*0c0d06caSMauro Carvalho Chehab static int uvc_clock_param_get(char *buffer, struct kernel_param *kp) 2011*0c0d06caSMauro Carvalho Chehab { 2012*0c0d06caSMauro Carvalho Chehab if (uvc_clock_param == CLOCK_MONOTONIC) 2013*0c0d06caSMauro Carvalho Chehab return sprintf(buffer, "CLOCK_MONOTONIC"); 2014*0c0d06caSMauro Carvalho Chehab else 2015*0c0d06caSMauro Carvalho Chehab return sprintf(buffer, "CLOCK_REALTIME"); 2016*0c0d06caSMauro Carvalho Chehab } 2017*0c0d06caSMauro Carvalho Chehab 2018*0c0d06caSMauro Carvalho Chehab static int uvc_clock_param_set(const char *val, struct kernel_param *kp) 2019*0c0d06caSMauro Carvalho Chehab { 2020*0c0d06caSMauro Carvalho Chehab if (strncasecmp(val, "clock_", strlen("clock_")) == 0) 2021*0c0d06caSMauro Carvalho Chehab val += strlen("clock_"); 2022*0c0d06caSMauro Carvalho Chehab 2023*0c0d06caSMauro Carvalho Chehab if (strcasecmp(val, "monotonic") == 0) 2024*0c0d06caSMauro Carvalho Chehab uvc_clock_param = CLOCK_MONOTONIC; 2025*0c0d06caSMauro Carvalho Chehab else if (strcasecmp(val, "realtime") == 0) 2026*0c0d06caSMauro Carvalho Chehab uvc_clock_param = CLOCK_REALTIME; 2027*0c0d06caSMauro Carvalho Chehab else 2028*0c0d06caSMauro Carvalho Chehab return -EINVAL; 2029*0c0d06caSMauro Carvalho Chehab 2030*0c0d06caSMauro Carvalho Chehab return 0; 2031*0c0d06caSMauro Carvalho Chehab } 2032*0c0d06caSMauro Carvalho Chehab 2033*0c0d06caSMauro Carvalho Chehab module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get, 2034*0c0d06caSMauro Carvalho Chehab &uvc_clock_param, S_IRUGO|S_IWUSR); 2035*0c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(clock, "Video buffers timestamp clock"); 2036*0c0d06caSMauro Carvalho Chehab module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR); 2037*0c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); 2038*0c0d06caSMauro Carvalho Chehab module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); 2039*0c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(quirks, "Forced device quirks"); 2040*0c0d06caSMauro Carvalho Chehab module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR); 2041*0c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(trace, "Trace level bitmask"); 2042*0c0d06caSMauro Carvalho Chehab module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR); 2043*0c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); 2044*0c0d06caSMauro Carvalho Chehab 2045*0c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 2046*0c0d06caSMauro Carvalho Chehab * Driver initialization and cleanup 2047*0c0d06caSMauro Carvalho Chehab */ 2048*0c0d06caSMauro Carvalho Chehab 2049*0c0d06caSMauro Carvalho Chehab /* 2050*0c0d06caSMauro Carvalho Chehab * The Logitech cameras listed below have their interface class set to 2051*0c0d06caSMauro Carvalho Chehab * VENDOR_SPEC because they don't announce themselves as UVC devices, even 2052*0c0d06caSMauro Carvalho Chehab * though they are compliant. 2053*0c0d06caSMauro Carvalho Chehab */ 2054*0c0d06caSMauro Carvalho Chehab static struct usb_device_id uvc_ids[] = { 2055*0c0d06caSMauro Carvalho Chehab /* LogiLink Wireless Webcam */ 2056*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2057*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2058*0c0d06caSMauro Carvalho Chehab .idVendor = 0x0416, 2059*0c0d06caSMauro Carvalho Chehab .idProduct = 0xa91a, 2060*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2061*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2062*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2063*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 2064*0c0d06caSMauro Carvalho Chehab /* Genius eFace 2025 */ 2065*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2066*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2067*0c0d06caSMauro Carvalho Chehab .idVendor = 0x0458, 2068*0c0d06caSMauro Carvalho Chehab .idProduct = 0x706e, 2069*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2070*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2071*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2072*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 2073*0c0d06caSMauro Carvalho Chehab /* Microsoft Lifecam NX-6000 */ 2074*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2075*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2076*0c0d06caSMauro Carvalho Chehab .idVendor = 0x045e, 2077*0c0d06caSMauro Carvalho Chehab .idProduct = 0x00f8, 2078*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2079*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2080*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2081*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 2082*0c0d06caSMauro Carvalho Chehab /* Microsoft Lifecam VX-7000 */ 2083*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2084*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2085*0c0d06caSMauro Carvalho Chehab .idVendor = 0x045e, 2086*0c0d06caSMauro Carvalho Chehab .idProduct = 0x0723, 2087*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2088*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2089*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2090*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 2091*0c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Fusion */ 2092*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2093*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2094*0c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 2095*0c0d06caSMauro Carvalho Chehab .idProduct = 0x08c1, 2096*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 2097*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2098*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 2099*0c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Orbit MP */ 2100*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2101*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2102*0c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 2103*0c0d06caSMauro Carvalho Chehab .idProduct = 0x08c2, 2104*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 2105*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2106*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 2107*0c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Pro for Notebook */ 2108*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2109*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2110*0c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 2111*0c0d06caSMauro Carvalho Chehab .idProduct = 0x08c3, 2112*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 2113*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2114*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 2115*0c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Pro 5000 */ 2116*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2117*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2118*0c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 2119*0c0d06caSMauro Carvalho Chehab .idProduct = 0x08c5, 2120*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 2121*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2122*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 2123*0c0d06caSMauro Carvalho Chehab /* Logitech Quickcam OEM Dell Notebook */ 2124*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2125*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2126*0c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 2127*0c0d06caSMauro Carvalho Chehab .idProduct = 0x08c6, 2128*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 2129*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2130*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 2131*0c0d06caSMauro Carvalho Chehab /* Logitech Quickcam OEM Cisco VT Camera II */ 2132*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2133*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2134*0c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 2135*0c0d06caSMauro Carvalho Chehab .idProduct = 0x08c7, 2136*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 2137*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2138*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 2139*0c0d06caSMauro Carvalho Chehab /* Chicony CNF7129 (Asus EEE 100HE) */ 2140*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2141*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2142*0c0d06caSMauro Carvalho Chehab .idVendor = 0x04f2, 2143*0c0d06caSMauro Carvalho Chehab .idProduct = 0xb071, 2144*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2145*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2146*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2147*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_RESTRICT_FRAME_RATE }, 2148*0c0d06caSMauro Carvalho Chehab /* Alcor Micro AU3820 (Future Boy PC USB Webcam) */ 2149*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2150*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2151*0c0d06caSMauro Carvalho Chehab .idVendor = 0x058f, 2152*0c0d06caSMauro Carvalho Chehab .idProduct = 0x3820, 2153*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2154*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2155*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2156*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 2157*0c0d06caSMauro Carvalho Chehab /* Dell XPS m1530 */ 2158*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2159*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2160*0c0d06caSMauro Carvalho Chehab .idVendor = 0x05a9, 2161*0c0d06caSMauro Carvalho Chehab .idProduct = 0x2640, 2162*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2163*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2164*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2165*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_DEF }, 2166*0c0d06caSMauro Carvalho Chehab /* Apple Built-In iSight */ 2167*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2168*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2169*0c0d06caSMauro Carvalho Chehab .idVendor = 0x05ac, 2170*0c0d06caSMauro Carvalho Chehab .idProduct = 0x8501, 2171*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2172*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2173*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2174*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX 2175*0c0d06caSMauro Carvalho Chehab | UVC_QUIRK_BUILTIN_ISIGHT }, 2176*0c0d06caSMauro Carvalho Chehab /* Foxlink ("HP Webcam" on HP Mini 5103) */ 2177*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2178*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2179*0c0d06caSMauro Carvalho Chehab .idVendor = 0x05c8, 2180*0c0d06caSMauro Carvalho Chehab .idProduct = 0x0403, 2181*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2182*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2183*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2184*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, 2185*0c0d06caSMauro Carvalho Chehab /* Genesys Logic USB 2.0 PC Camera */ 2186*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2187*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2188*0c0d06caSMauro Carvalho Chehab .idVendor = 0x05e3, 2189*0c0d06caSMauro Carvalho Chehab .idProduct = 0x0505, 2190*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2191*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2192*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2193*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 2194*0c0d06caSMauro Carvalho Chehab /* Hercules Classic Silver */ 2195*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2196*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2197*0c0d06caSMauro Carvalho Chehab .idVendor = 0x06f8, 2198*0c0d06caSMauro Carvalho Chehab .idProduct = 0x300c, 2199*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2200*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2201*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2202*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, 2203*0c0d06caSMauro Carvalho Chehab /* ViMicro Vega */ 2204*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2205*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2206*0c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 2207*0c0d06caSMauro Carvalho Chehab .idProduct = 0x332d, 2208*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2209*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2210*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2211*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, 2212*0c0d06caSMauro Carvalho Chehab /* ViMicro - Minoru3D */ 2213*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2214*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2215*0c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 2216*0c0d06caSMauro Carvalho Chehab .idProduct = 0x3410, 2217*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2218*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2219*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2220*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, 2221*0c0d06caSMauro Carvalho Chehab /* ViMicro Venus - Minoru3D */ 2222*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2223*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2224*0c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 2225*0c0d06caSMauro Carvalho Chehab .idProduct = 0x3420, 2226*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2227*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2228*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2229*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, 2230*0c0d06caSMauro Carvalho Chehab /* Ophir Optronics - SPCAM 620U */ 2231*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2232*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2233*0c0d06caSMauro Carvalho Chehab .idVendor = 0x0bd3, 2234*0c0d06caSMauro Carvalho Chehab .idProduct = 0x0555, 2235*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2236*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2237*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2238*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 2239*0c0d06caSMauro Carvalho Chehab /* MT6227 */ 2240*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2241*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2242*0c0d06caSMauro Carvalho Chehab .idVendor = 0x0e8d, 2243*0c0d06caSMauro Carvalho Chehab .idProduct = 0x0004, 2244*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2245*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2246*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2247*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX 2248*0c0d06caSMauro Carvalho Chehab | UVC_QUIRK_PROBE_DEF }, 2249*0c0d06caSMauro Carvalho Chehab /* IMC Networks (Medion Akoya) */ 2250*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2251*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2252*0c0d06caSMauro Carvalho Chehab .idVendor = 0x13d3, 2253*0c0d06caSMauro Carvalho Chehab .idProduct = 0x5103, 2254*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2255*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2256*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2257*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 2258*0c0d06caSMauro Carvalho Chehab /* JMicron USB2.0 XGA WebCam */ 2259*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2260*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2261*0c0d06caSMauro Carvalho Chehab .idVendor = 0x152d, 2262*0c0d06caSMauro Carvalho Chehab .idProduct = 0x0310, 2263*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2264*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2265*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2266*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 2267*0c0d06caSMauro Carvalho Chehab /* Syntek (HP Spartan) */ 2268*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2269*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2270*0c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 2271*0c0d06caSMauro Carvalho Chehab .idProduct = 0x5212, 2272*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2273*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2274*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2275*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 2276*0c0d06caSMauro Carvalho Chehab /* Syntek (Samsung Q310) */ 2277*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2278*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2279*0c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 2280*0c0d06caSMauro Carvalho Chehab .idProduct = 0x5931, 2281*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2282*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2283*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2284*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 2285*0c0d06caSMauro Carvalho Chehab /* Syntek (Packard Bell EasyNote MX52 */ 2286*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2287*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2288*0c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 2289*0c0d06caSMauro Carvalho Chehab .idProduct = 0x8a12, 2290*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2291*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2292*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2293*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 2294*0c0d06caSMauro Carvalho Chehab /* Syntek (Asus F9SG) */ 2295*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2296*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2297*0c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 2298*0c0d06caSMauro Carvalho Chehab .idProduct = 0x8a31, 2299*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2300*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2301*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2302*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 2303*0c0d06caSMauro Carvalho Chehab /* Syntek (Asus U3S) */ 2304*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2305*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2306*0c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 2307*0c0d06caSMauro Carvalho Chehab .idProduct = 0x8a33, 2308*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2309*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2310*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2311*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 2312*0c0d06caSMauro Carvalho Chehab /* Syntek (JAOtech Smart Terminal) */ 2313*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2314*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2315*0c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 2316*0c0d06caSMauro Carvalho Chehab .idProduct = 0x8a34, 2317*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2318*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2319*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2320*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 2321*0c0d06caSMauro Carvalho Chehab /* Miricle 307K */ 2322*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2323*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2324*0c0d06caSMauro Carvalho Chehab .idVendor = 0x17dc, 2325*0c0d06caSMauro Carvalho Chehab .idProduct = 0x0202, 2326*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2327*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2328*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2329*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 2330*0c0d06caSMauro Carvalho Chehab /* Lenovo Thinkpad SL400/SL500 */ 2331*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2332*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2333*0c0d06caSMauro Carvalho Chehab .idVendor = 0x17ef, 2334*0c0d06caSMauro Carvalho Chehab .idProduct = 0x480b, 2335*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2336*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2337*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2338*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 2339*0c0d06caSMauro Carvalho Chehab /* Aveo Technology USB 2.0 Camera */ 2340*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2341*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2342*0c0d06caSMauro Carvalho Chehab .idVendor = 0x1871, 2343*0c0d06caSMauro Carvalho Chehab .idProduct = 0x0306, 2344*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2345*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2346*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2347*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX 2348*0c0d06caSMauro Carvalho Chehab | UVC_QUIRK_PROBE_EXTRAFIELDS }, 2349*0c0d06caSMauro Carvalho Chehab /* Ecamm Pico iMage */ 2350*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2351*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2352*0c0d06caSMauro Carvalho Chehab .idVendor = 0x18cd, 2353*0c0d06caSMauro Carvalho Chehab .idProduct = 0xcafe, 2354*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2355*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2356*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2357*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS }, 2358*0c0d06caSMauro Carvalho Chehab /* Manta MM-353 Plako */ 2359*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2360*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2361*0c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 2362*0c0d06caSMauro Carvalho Chehab .idProduct = 0x3188, 2363*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2364*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2365*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2366*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 2367*0c0d06caSMauro Carvalho Chehab /* FSC WebCam V30S */ 2368*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2369*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2370*0c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 2371*0c0d06caSMauro Carvalho Chehab .idProduct = 0x3288, 2372*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2373*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2374*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2375*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 2376*0c0d06caSMauro Carvalho Chehab /* Arkmicro unbranded */ 2377*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2378*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2379*0c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 2380*0c0d06caSMauro Carvalho Chehab .idProduct = 0x3290, 2381*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2382*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2383*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2384*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_DEF }, 2385*0c0d06caSMauro Carvalho Chehab /* The Imaging Source USB CCD cameras */ 2386*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2387*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2388*0c0d06caSMauro Carvalho Chehab .idVendor = 0x199e, 2389*0c0d06caSMauro Carvalho Chehab .idProduct = 0x8102, 2390*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 2391*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2392*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 2393*0c0d06caSMauro Carvalho Chehab /* Bodelin ProScopeHR */ 2394*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2395*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_DEV_HI 2396*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2397*0c0d06caSMauro Carvalho Chehab .idVendor = 0x19ab, 2398*0c0d06caSMauro Carvalho Chehab .idProduct = 0x1000, 2399*0c0d06caSMauro Carvalho Chehab .bcdDevice_hi = 0x0126, 2400*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2401*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2402*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2403*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STATUS_INTERVAL }, 2404*0c0d06caSMauro Carvalho Chehab /* MSI StarCam 370i */ 2405*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2406*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2407*0c0d06caSMauro Carvalho Chehab .idVendor = 0x1b3b, 2408*0c0d06caSMauro Carvalho Chehab .idProduct = 0x2951, 2409*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2410*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2411*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2412*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 2413*0c0d06caSMauro Carvalho Chehab /* SiGma Micro USB Web Camera */ 2414*0c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2415*0c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 2416*0c0d06caSMauro Carvalho Chehab .idVendor = 0x1c4f, 2417*0c0d06caSMauro Carvalho Chehab .idProduct = 0x3000, 2418*0c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 2419*0c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 2420*0c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 2421*0c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX 2422*0c0d06caSMauro Carvalho Chehab | UVC_QUIRK_IGNORE_SELECTOR_UNIT }, 2423*0c0d06caSMauro Carvalho Chehab /* Generic USB Video Class */ 2424*0c0d06caSMauro Carvalho Chehab { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, 2425*0c0d06caSMauro Carvalho Chehab {} 2426*0c0d06caSMauro Carvalho Chehab }; 2427*0c0d06caSMauro Carvalho Chehab 2428*0c0d06caSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, uvc_ids); 2429*0c0d06caSMauro Carvalho Chehab 2430*0c0d06caSMauro Carvalho Chehab struct uvc_driver uvc_driver = { 2431*0c0d06caSMauro Carvalho Chehab .driver = { 2432*0c0d06caSMauro Carvalho Chehab .name = "uvcvideo", 2433*0c0d06caSMauro Carvalho Chehab .probe = uvc_probe, 2434*0c0d06caSMauro Carvalho Chehab .disconnect = uvc_disconnect, 2435*0c0d06caSMauro Carvalho Chehab .suspend = uvc_suspend, 2436*0c0d06caSMauro Carvalho Chehab .resume = uvc_resume, 2437*0c0d06caSMauro Carvalho Chehab .reset_resume = uvc_reset_resume, 2438*0c0d06caSMauro Carvalho Chehab .id_table = uvc_ids, 2439*0c0d06caSMauro Carvalho Chehab .supports_autosuspend = 1, 2440*0c0d06caSMauro Carvalho Chehab }, 2441*0c0d06caSMauro Carvalho Chehab }; 2442*0c0d06caSMauro Carvalho Chehab 2443*0c0d06caSMauro Carvalho Chehab static int __init uvc_init(void) 2444*0c0d06caSMauro Carvalho Chehab { 2445*0c0d06caSMauro Carvalho Chehab int ret; 2446*0c0d06caSMauro Carvalho Chehab 2447*0c0d06caSMauro Carvalho Chehab uvc_debugfs_init(); 2448*0c0d06caSMauro Carvalho Chehab 2449*0c0d06caSMauro Carvalho Chehab ret = usb_register(&uvc_driver.driver); 2450*0c0d06caSMauro Carvalho Chehab if (ret < 0) { 2451*0c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup(); 2452*0c0d06caSMauro Carvalho Chehab return ret; 2453*0c0d06caSMauro Carvalho Chehab } 2454*0c0d06caSMauro Carvalho Chehab 2455*0c0d06caSMauro Carvalho Chehab printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n"); 2456*0c0d06caSMauro Carvalho Chehab return 0; 2457*0c0d06caSMauro Carvalho Chehab } 2458*0c0d06caSMauro Carvalho Chehab 2459*0c0d06caSMauro Carvalho Chehab static void __exit uvc_cleanup(void) 2460*0c0d06caSMauro Carvalho Chehab { 2461*0c0d06caSMauro Carvalho Chehab usb_deregister(&uvc_driver.driver); 2462*0c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup(); 2463*0c0d06caSMauro Carvalho Chehab } 2464*0c0d06caSMauro Carvalho Chehab 2465*0c0d06caSMauro Carvalho Chehab module_init(uvc_init); 2466*0c0d06caSMauro Carvalho Chehab module_exit(uvc_cleanup); 2467*0c0d06caSMauro Carvalho Chehab 2468*0c0d06caSMauro Carvalho Chehab MODULE_AUTHOR(DRIVER_AUTHOR); 2469*0c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION(DRIVER_DESC); 2470*0c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 2471*0c0d06caSMauro Carvalho Chehab MODULE_VERSION(DRIVER_VERSION); 2472*0c0d06caSMauro Carvalho Chehab 2473