10c0d06caSMauro Carvalho Chehab /* 20c0d06caSMauro Carvalho Chehab * uvc_driver.c -- USB Video Class driver 30c0d06caSMauro Carvalho Chehab * 40c0d06caSMauro Carvalho Chehab * Copyright (C) 2005-2010 50c0d06caSMauro Carvalho Chehab * Laurent Pinchart (laurent.pinchart@ideasonboard.com) 60c0d06caSMauro Carvalho Chehab * 70c0d06caSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 80c0d06caSMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 90c0d06caSMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 100c0d06caSMauro Carvalho Chehab * (at your option) any later version. 110c0d06caSMauro Carvalho Chehab * 120c0d06caSMauro Carvalho Chehab */ 130c0d06caSMauro Carvalho Chehab 140c0d06caSMauro Carvalho Chehab #include <linux/atomic.h> 150c0d06caSMauro Carvalho Chehab #include <linux/kernel.h> 160c0d06caSMauro Carvalho Chehab #include <linux/list.h> 170c0d06caSMauro Carvalho Chehab #include <linux/module.h> 180c0d06caSMauro Carvalho Chehab #include <linux/slab.h> 190c0d06caSMauro Carvalho Chehab #include <linux/usb.h> 200c0d06caSMauro Carvalho Chehab #include <linux/videodev2.h> 210c0d06caSMauro Carvalho Chehab #include <linux/vmalloc.h> 220c0d06caSMauro Carvalho Chehab #include <linux/wait.h> 230c0d06caSMauro Carvalho Chehab #include <linux/version.h> 240c0d06caSMauro Carvalho Chehab #include <asm/unaligned.h> 250c0d06caSMauro Carvalho Chehab 260c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h> 270c0d06caSMauro Carvalho Chehab 280c0d06caSMauro Carvalho Chehab #include "uvcvideo.h" 290c0d06caSMauro Carvalho Chehab 300c0d06caSMauro Carvalho Chehab #define DRIVER_AUTHOR "Laurent Pinchart " \ 310c0d06caSMauro Carvalho Chehab "<laurent.pinchart@ideasonboard.com>" 320c0d06caSMauro Carvalho Chehab #define DRIVER_DESC "USB Video Class driver" 330c0d06caSMauro Carvalho Chehab 340c0d06caSMauro Carvalho Chehab unsigned int uvc_clock_param = CLOCK_MONOTONIC; 350c0d06caSMauro Carvalho Chehab unsigned int uvc_no_drop_param; 360c0d06caSMauro Carvalho Chehab static unsigned int uvc_quirks_param = -1; 370c0d06caSMauro Carvalho Chehab unsigned int uvc_trace_param; 380c0d06caSMauro Carvalho Chehab unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; 390c0d06caSMauro Carvalho Chehab 400c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 410c0d06caSMauro Carvalho Chehab * Video formats 420c0d06caSMauro Carvalho Chehab */ 430c0d06caSMauro Carvalho Chehab 440c0d06caSMauro Carvalho Chehab static struct uvc_format_desc uvc_fmts[] = { 450c0d06caSMauro Carvalho Chehab { 460c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:2 (YUYV)", 470c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_YUY2, 480c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_YUYV, 490c0d06caSMauro Carvalho Chehab }, 500c0d06caSMauro Carvalho Chehab { 510c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:2 (YUYV)", 520c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_YUY2_ISIGHT, 530c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_YUYV, 540c0d06caSMauro Carvalho Chehab }, 550c0d06caSMauro Carvalho Chehab { 560c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:0 (NV12)", 570c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_NV12, 580c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_NV12, 590c0d06caSMauro Carvalho Chehab }, 600c0d06caSMauro Carvalho Chehab { 610c0d06caSMauro Carvalho Chehab .name = "MJPEG", 620c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_MJPEG, 630c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_MJPEG, 640c0d06caSMauro Carvalho Chehab }, 650c0d06caSMauro Carvalho Chehab { 660c0d06caSMauro Carvalho Chehab .name = "YVU 4:2:0 (YV12)", 670c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_YV12, 680c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_YVU420, 690c0d06caSMauro Carvalho Chehab }, 700c0d06caSMauro Carvalho Chehab { 710c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:0 (I420)", 720c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_I420, 730c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_YUV420, 740c0d06caSMauro Carvalho Chehab }, 750c0d06caSMauro Carvalho Chehab { 760c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:0 (M420)", 770c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_M420, 780c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_M420, 790c0d06caSMauro Carvalho Chehab }, 800c0d06caSMauro Carvalho Chehab { 810c0d06caSMauro Carvalho Chehab .name = "YUV 4:2:2 (UYVY)", 820c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_UYVY, 830c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_UYVY, 840c0d06caSMauro Carvalho Chehab }, 850c0d06caSMauro Carvalho Chehab { 860c0d06caSMauro Carvalho Chehab .name = "Greyscale 8-bit (Y800)", 870c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y800, 880c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_GREY, 890c0d06caSMauro Carvalho Chehab }, 900c0d06caSMauro Carvalho Chehab { 910c0d06caSMauro Carvalho Chehab .name = "Greyscale 8-bit (Y8 )", 920c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y8, 930c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_GREY, 940c0d06caSMauro Carvalho Chehab }, 950c0d06caSMauro Carvalho Chehab { 960c0d06caSMauro Carvalho Chehab .name = "Greyscale 10-bit (Y10 )", 970c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y10, 980c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_Y10, 990c0d06caSMauro Carvalho Chehab }, 1000c0d06caSMauro Carvalho Chehab { 1010c0d06caSMauro Carvalho Chehab .name = "Greyscale 12-bit (Y12 )", 1020c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y12, 1030c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_Y12, 1040c0d06caSMauro Carvalho Chehab }, 1050c0d06caSMauro Carvalho Chehab { 1060c0d06caSMauro Carvalho Chehab .name = "Greyscale 16-bit (Y16 )", 1070c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_Y16, 1080c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_Y16, 1090c0d06caSMauro Carvalho Chehab }, 1100c0d06caSMauro Carvalho Chehab { 1110c0d06caSMauro Carvalho Chehab .name = "RGB Bayer", 1120c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_BY8, 1130c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_SBGGR8, 1140c0d06caSMauro Carvalho Chehab }, 1150c0d06caSMauro Carvalho Chehab { 1160c0d06caSMauro Carvalho Chehab .name = "RGB565", 1170c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_RGBP, 1180c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_RGB565, 1190c0d06caSMauro Carvalho Chehab }, 1200c0d06caSMauro Carvalho Chehab { 1210c0d06caSMauro Carvalho Chehab .name = "H.264", 1220c0d06caSMauro Carvalho Chehab .guid = UVC_GUID_FORMAT_H264, 1230c0d06caSMauro Carvalho Chehab .fcc = V4L2_PIX_FMT_H264, 1240c0d06caSMauro Carvalho Chehab }, 1250c0d06caSMauro Carvalho Chehab }; 1260c0d06caSMauro Carvalho Chehab 1270c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 1280c0d06caSMauro Carvalho Chehab * Utility functions 1290c0d06caSMauro Carvalho Chehab */ 1300c0d06caSMauro Carvalho Chehab 1310c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, 1320c0d06caSMauro Carvalho Chehab __u8 epaddr) 1330c0d06caSMauro Carvalho Chehab { 1340c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep; 1350c0d06caSMauro Carvalho Chehab unsigned int i; 1360c0d06caSMauro Carvalho Chehab 1370c0d06caSMauro Carvalho Chehab for (i = 0; i < alts->desc.bNumEndpoints; ++i) { 1380c0d06caSMauro Carvalho Chehab ep = &alts->endpoint[i]; 1390c0d06caSMauro Carvalho Chehab if (ep->desc.bEndpointAddress == epaddr) 1400c0d06caSMauro Carvalho Chehab return ep; 1410c0d06caSMauro Carvalho Chehab } 1420c0d06caSMauro Carvalho Chehab 1430c0d06caSMauro Carvalho Chehab return NULL; 1440c0d06caSMauro Carvalho Chehab } 1450c0d06caSMauro Carvalho Chehab 1460c0d06caSMauro Carvalho Chehab static struct uvc_format_desc *uvc_format_by_guid(const __u8 guid[16]) 1470c0d06caSMauro Carvalho Chehab { 1480c0d06caSMauro Carvalho Chehab unsigned int len = ARRAY_SIZE(uvc_fmts); 1490c0d06caSMauro Carvalho Chehab unsigned int i; 1500c0d06caSMauro Carvalho Chehab 1510c0d06caSMauro Carvalho Chehab for (i = 0; i < len; ++i) { 1520c0d06caSMauro Carvalho Chehab if (memcmp(guid, uvc_fmts[i].guid, 16) == 0) 1530c0d06caSMauro Carvalho Chehab return &uvc_fmts[i]; 1540c0d06caSMauro Carvalho Chehab } 1550c0d06caSMauro Carvalho Chehab 1560c0d06caSMauro Carvalho Chehab return NULL; 1570c0d06caSMauro Carvalho Chehab } 1580c0d06caSMauro Carvalho Chehab 1590c0d06caSMauro Carvalho Chehab static __u32 uvc_colorspace(const __u8 primaries) 1600c0d06caSMauro Carvalho Chehab { 1610c0d06caSMauro Carvalho Chehab static const __u8 colorprimaries[] = { 1620c0d06caSMauro Carvalho Chehab 0, 1630c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SRGB, 1640c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_470_SYSTEM_M, 1650c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_470_SYSTEM_BG, 1660c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SMPTE170M, 1670c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SMPTE240M, 1680c0d06caSMauro Carvalho Chehab }; 1690c0d06caSMauro Carvalho Chehab 1700c0d06caSMauro Carvalho Chehab if (primaries < ARRAY_SIZE(colorprimaries)) 1710c0d06caSMauro Carvalho Chehab return colorprimaries[primaries]; 1720c0d06caSMauro Carvalho Chehab 1730c0d06caSMauro Carvalho Chehab return 0; 1740c0d06caSMauro Carvalho Chehab } 1750c0d06caSMauro Carvalho Chehab 1760c0d06caSMauro Carvalho Chehab /* Simplify a fraction using a simple continued fraction decomposition. The 1770c0d06caSMauro Carvalho Chehab * idea here is to convert fractions such as 333333/10000000 to 1/30 using 1780c0d06caSMauro Carvalho Chehab * 32 bit arithmetic only. The algorithm is not perfect and relies upon two 1790c0d06caSMauro Carvalho Chehab * arbitrary parameters to remove non-significative terms from the simple 1800c0d06caSMauro Carvalho Chehab * continued fraction decomposition. Using 8 and 333 for n_terms and threshold 1810c0d06caSMauro Carvalho Chehab * respectively seems to give nice results. 1820c0d06caSMauro Carvalho Chehab */ 1830c0d06caSMauro Carvalho Chehab void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, 1840c0d06caSMauro Carvalho Chehab unsigned int n_terms, unsigned int threshold) 1850c0d06caSMauro Carvalho Chehab { 1860c0d06caSMauro Carvalho Chehab uint32_t *an; 1870c0d06caSMauro Carvalho Chehab uint32_t x, y, r; 1880c0d06caSMauro Carvalho Chehab unsigned int i, n; 1890c0d06caSMauro Carvalho Chehab 1900c0d06caSMauro Carvalho Chehab an = kmalloc(n_terms * sizeof *an, GFP_KERNEL); 1910c0d06caSMauro Carvalho Chehab if (an == NULL) 1920c0d06caSMauro Carvalho Chehab return; 1930c0d06caSMauro Carvalho Chehab 1940c0d06caSMauro Carvalho Chehab /* Convert the fraction to a simple continued fraction. See 1950c0d06caSMauro Carvalho Chehab * http://mathforum.org/dr.math/faq/faq.fractions.html 1960c0d06caSMauro Carvalho Chehab * Stop if the current term is bigger than or equal to the given 1970c0d06caSMauro Carvalho Chehab * threshold. 1980c0d06caSMauro Carvalho Chehab */ 1990c0d06caSMauro Carvalho Chehab x = *numerator; 2000c0d06caSMauro Carvalho Chehab y = *denominator; 2010c0d06caSMauro Carvalho Chehab 2020c0d06caSMauro Carvalho Chehab for (n = 0; n < n_terms && y != 0; ++n) { 2030c0d06caSMauro Carvalho Chehab an[n] = x / y; 2040c0d06caSMauro Carvalho Chehab if (an[n] >= threshold) { 2050c0d06caSMauro Carvalho Chehab if (n < 2) 2060c0d06caSMauro Carvalho Chehab n++; 2070c0d06caSMauro Carvalho Chehab break; 2080c0d06caSMauro Carvalho Chehab } 2090c0d06caSMauro Carvalho Chehab 2100c0d06caSMauro Carvalho Chehab r = x - an[n] * y; 2110c0d06caSMauro Carvalho Chehab x = y; 2120c0d06caSMauro Carvalho Chehab y = r; 2130c0d06caSMauro Carvalho Chehab } 2140c0d06caSMauro Carvalho Chehab 2150c0d06caSMauro Carvalho Chehab /* Expand the simple continued fraction back to an integer fraction. */ 2160c0d06caSMauro Carvalho Chehab x = 0; 2170c0d06caSMauro Carvalho Chehab y = 1; 2180c0d06caSMauro Carvalho Chehab 2190c0d06caSMauro Carvalho Chehab for (i = n; i > 0; --i) { 2200c0d06caSMauro Carvalho Chehab r = y; 2210c0d06caSMauro Carvalho Chehab y = an[i-1] * y + x; 2220c0d06caSMauro Carvalho Chehab x = r; 2230c0d06caSMauro Carvalho Chehab } 2240c0d06caSMauro Carvalho Chehab 2250c0d06caSMauro Carvalho Chehab *numerator = y; 2260c0d06caSMauro Carvalho Chehab *denominator = x; 2270c0d06caSMauro Carvalho Chehab kfree(an); 2280c0d06caSMauro Carvalho Chehab } 2290c0d06caSMauro Carvalho Chehab 2300c0d06caSMauro Carvalho Chehab /* Convert a fraction to a frame interval in 100ns multiples. The idea here is 2310c0d06caSMauro Carvalho Chehab * to compute numerator / denominator * 10000000 using 32 bit fixed point 2320c0d06caSMauro Carvalho Chehab * arithmetic only. 2330c0d06caSMauro Carvalho Chehab */ 2340c0d06caSMauro Carvalho Chehab uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator) 2350c0d06caSMauro Carvalho Chehab { 2360c0d06caSMauro Carvalho Chehab uint32_t multiplier; 2370c0d06caSMauro Carvalho Chehab 2380c0d06caSMauro Carvalho Chehab /* Saturate the result if the operation would overflow. */ 2390c0d06caSMauro Carvalho Chehab if (denominator == 0 || 2400c0d06caSMauro Carvalho Chehab numerator/denominator >= ((uint32_t)-1)/10000000) 2410c0d06caSMauro Carvalho Chehab return (uint32_t)-1; 2420c0d06caSMauro Carvalho Chehab 2430c0d06caSMauro Carvalho Chehab /* Divide both the denominator and the multiplier by two until 2440c0d06caSMauro Carvalho Chehab * numerator * multiplier doesn't overflow. If anyone knows a better 2450c0d06caSMauro Carvalho Chehab * algorithm please let me know. 2460c0d06caSMauro Carvalho Chehab */ 2470c0d06caSMauro Carvalho Chehab multiplier = 10000000; 2480c0d06caSMauro Carvalho Chehab while (numerator > ((uint32_t)-1)/multiplier) { 2490c0d06caSMauro Carvalho Chehab multiplier /= 2; 2500c0d06caSMauro Carvalho Chehab denominator /= 2; 2510c0d06caSMauro Carvalho Chehab } 2520c0d06caSMauro Carvalho Chehab 2530c0d06caSMauro Carvalho Chehab return denominator ? numerator * multiplier / denominator : 0; 2540c0d06caSMauro Carvalho Chehab } 2550c0d06caSMauro Carvalho Chehab 2560c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 2570c0d06caSMauro Carvalho Chehab * Terminal and unit management 2580c0d06caSMauro Carvalho Chehab */ 2590c0d06caSMauro Carvalho Chehab 2600c0d06caSMauro Carvalho Chehab struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id) 2610c0d06caSMauro Carvalho Chehab { 2620c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 2630c0d06caSMauro Carvalho Chehab 2640c0d06caSMauro Carvalho Chehab list_for_each_entry(entity, &dev->entities, list) { 2650c0d06caSMauro Carvalho Chehab if (entity->id == id) 2660c0d06caSMauro Carvalho Chehab return entity; 2670c0d06caSMauro Carvalho Chehab } 2680c0d06caSMauro Carvalho Chehab 2690c0d06caSMauro Carvalho Chehab return NULL; 2700c0d06caSMauro Carvalho Chehab } 2710c0d06caSMauro Carvalho Chehab 2720c0d06caSMauro Carvalho Chehab static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, 2730c0d06caSMauro Carvalho Chehab int id, struct uvc_entity *entity) 2740c0d06caSMauro Carvalho Chehab { 2750c0d06caSMauro Carvalho Chehab unsigned int i; 2760c0d06caSMauro Carvalho Chehab 2770c0d06caSMauro Carvalho Chehab if (entity == NULL) 2780c0d06caSMauro Carvalho Chehab entity = list_entry(&dev->entities, struct uvc_entity, list); 2790c0d06caSMauro Carvalho Chehab 2800c0d06caSMauro Carvalho Chehab list_for_each_entry_continue(entity, &dev->entities, list) { 2810c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->bNrInPins; ++i) 2820c0d06caSMauro Carvalho Chehab if (entity->baSourceID[i] == id) 2830c0d06caSMauro Carvalho Chehab return entity; 2840c0d06caSMauro Carvalho Chehab } 2850c0d06caSMauro Carvalho Chehab 2860c0d06caSMauro Carvalho Chehab return NULL; 2870c0d06caSMauro Carvalho Chehab } 2880c0d06caSMauro Carvalho Chehab 2890c0d06caSMauro Carvalho Chehab static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id) 2900c0d06caSMauro Carvalho Chehab { 2910c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 2920c0d06caSMauro Carvalho Chehab 2930c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 2940c0d06caSMauro Carvalho Chehab if (stream->header.bTerminalLink == id) 2950c0d06caSMauro Carvalho Chehab return stream; 2960c0d06caSMauro Carvalho Chehab } 2970c0d06caSMauro Carvalho Chehab 2980c0d06caSMauro Carvalho Chehab return NULL; 2990c0d06caSMauro Carvalho Chehab } 3000c0d06caSMauro Carvalho Chehab 3010c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 3020c0d06caSMauro Carvalho Chehab * Descriptors parsing 3030c0d06caSMauro Carvalho Chehab */ 3040c0d06caSMauro Carvalho Chehab 3050c0d06caSMauro Carvalho Chehab static int uvc_parse_format(struct uvc_device *dev, 3060c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming, struct uvc_format *format, 3070c0d06caSMauro Carvalho Chehab __u32 **intervals, unsigned char *buffer, int buflen) 3080c0d06caSMauro Carvalho Chehab { 3090c0d06caSMauro Carvalho Chehab struct usb_interface *intf = streaming->intf; 3100c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = intf->cur_altsetting; 3110c0d06caSMauro Carvalho Chehab struct uvc_format_desc *fmtdesc; 3120c0d06caSMauro Carvalho Chehab struct uvc_frame *frame; 3130c0d06caSMauro Carvalho Chehab const unsigned char *start = buffer; 3140c0d06caSMauro Carvalho Chehab unsigned int interval; 3150c0d06caSMauro Carvalho Chehab unsigned int i, n; 3160c0d06caSMauro Carvalho Chehab __u8 ftype; 3170c0d06caSMauro Carvalho Chehab 3180c0d06caSMauro Carvalho Chehab format->type = buffer[2]; 3190c0d06caSMauro Carvalho Chehab format->index = buffer[3]; 3200c0d06caSMauro Carvalho Chehab 3210c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 3220c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 3230c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 3240c0d06caSMauro Carvalho Chehab n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28; 3250c0d06caSMauro Carvalho Chehab if (buflen < n) { 3260c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 3270c0d06caSMauro Carvalho Chehab "interface %d FORMAT error\n", 3280c0d06caSMauro Carvalho Chehab dev->udev->devnum, 3290c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 3300c0d06caSMauro Carvalho Chehab return -EINVAL; 3310c0d06caSMauro Carvalho Chehab } 3320c0d06caSMauro Carvalho Chehab 3330c0d06caSMauro Carvalho Chehab /* Find the format descriptor from its GUID. */ 3340c0d06caSMauro Carvalho Chehab fmtdesc = uvc_format_by_guid(&buffer[5]); 3350c0d06caSMauro Carvalho Chehab 3360c0d06caSMauro Carvalho Chehab if (fmtdesc != NULL) { 3370c0d06caSMauro Carvalho Chehab strlcpy(format->name, fmtdesc->name, 3380c0d06caSMauro Carvalho Chehab sizeof format->name); 3390c0d06caSMauro Carvalho Chehab format->fcc = fmtdesc->fcc; 3400c0d06caSMauro Carvalho Chehab } else { 3410c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "Unknown video format %pUl\n", 3420c0d06caSMauro Carvalho Chehab &buffer[5]); 3430c0d06caSMauro Carvalho Chehab snprintf(format->name, sizeof(format->name), "%pUl\n", 3440c0d06caSMauro Carvalho Chehab &buffer[5]); 3450c0d06caSMauro Carvalho Chehab format->fcc = 0; 3460c0d06caSMauro Carvalho Chehab } 3470c0d06caSMauro Carvalho Chehab 3480c0d06caSMauro Carvalho Chehab format->bpp = buffer[21]; 3490c0d06caSMauro Carvalho Chehab if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) { 3500c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_UNCOMPRESSED; 3510c0d06caSMauro Carvalho Chehab } else { 3520c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_FRAME_BASED; 3530c0d06caSMauro Carvalho Chehab if (buffer[27]) 3540c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED; 3550c0d06caSMauro Carvalho Chehab } 3560c0d06caSMauro Carvalho Chehab break; 3570c0d06caSMauro Carvalho Chehab 3580c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 3590c0d06caSMauro Carvalho Chehab if (buflen < 11) { 3600c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 3610c0d06caSMauro Carvalho Chehab "interface %d FORMAT error\n", 3620c0d06caSMauro Carvalho Chehab dev->udev->devnum, 3630c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 3640c0d06caSMauro Carvalho Chehab return -EINVAL; 3650c0d06caSMauro Carvalho Chehab } 3660c0d06caSMauro Carvalho Chehab 3670c0d06caSMauro Carvalho Chehab strlcpy(format->name, "MJPEG", sizeof format->name); 3680c0d06caSMauro Carvalho Chehab format->fcc = V4L2_PIX_FMT_MJPEG; 3690c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED; 3700c0d06caSMauro Carvalho Chehab format->bpp = 0; 3710c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_MJPEG; 3720c0d06caSMauro Carvalho Chehab break; 3730c0d06caSMauro Carvalho Chehab 3740c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 3750c0d06caSMauro Carvalho Chehab if (buflen < 9) { 3760c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 3770c0d06caSMauro Carvalho Chehab "interface %d FORMAT error\n", 3780c0d06caSMauro Carvalho Chehab dev->udev->devnum, 3790c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 3800c0d06caSMauro Carvalho Chehab return -EINVAL; 3810c0d06caSMauro Carvalho Chehab } 3820c0d06caSMauro Carvalho Chehab 3830c0d06caSMauro Carvalho Chehab switch (buffer[8] & 0x7f) { 3840c0d06caSMauro Carvalho Chehab case 0: 3850c0d06caSMauro Carvalho Chehab strlcpy(format->name, "SD-DV", sizeof format->name); 3860c0d06caSMauro Carvalho Chehab break; 3870c0d06caSMauro Carvalho Chehab case 1: 3880c0d06caSMauro Carvalho Chehab strlcpy(format->name, "SDL-DV", sizeof format->name); 3890c0d06caSMauro Carvalho Chehab break; 3900c0d06caSMauro Carvalho Chehab case 2: 3910c0d06caSMauro Carvalho Chehab strlcpy(format->name, "HD-DV", sizeof format->name); 3920c0d06caSMauro Carvalho Chehab break; 3930c0d06caSMauro Carvalho Chehab default: 3940c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 3950c0d06caSMauro Carvalho Chehab "interface %d: unknown DV format %u\n", 3960c0d06caSMauro Carvalho Chehab dev->udev->devnum, 3970c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, buffer[8]); 3980c0d06caSMauro Carvalho Chehab return -EINVAL; 3990c0d06caSMauro Carvalho Chehab } 4000c0d06caSMauro Carvalho Chehab 4010c0d06caSMauro Carvalho Chehab strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz", 4020c0d06caSMauro Carvalho Chehab sizeof format->name); 4030c0d06caSMauro Carvalho Chehab 4040c0d06caSMauro Carvalho Chehab format->fcc = V4L2_PIX_FMT_DV; 4050c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM; 4060c0d06caSMauro Carvalho Chehab format->bpp = 0; 4070c0d06caSMauro Carvalho Chehab ftype = 0; 4080c0d06caSMauro Carvalho Chehab 4090c0d06caSMauro Carvalho Chehab /* Create a dummy frame descriptor. */ 4100c0d06caSMauro Carvalho Chehab frame = &format->frame[0]; 4110c0d06caSMauro Carvalho Chehab memset(&format->frame[0], 0, sizeof format->frame[0]); 4120c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = 1; 4130c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 1; 4140c0d06caSMauro Carvalho Chehab frame->dwFrameInterval = *intervals; 4150c0d06caSMauro Carvalho Chehab *(*intervals)++ = 1; 4160c0d06caSMauro Carvalho Chehab format->nframes = 1; 4170c0d06caSMauro Carvalho Chehab break; 4180c0d06caSMauro Carvalho Chehab 4190c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MPEG2TS: 4200c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_STREAM_BASED: 4210c0d06caSMauro Carvalho Chehab /* Not supported yet. */ 4220c0d06caSMauro Carvalho Chehab default: 4230c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 4240c0d06caSMauro Carvalho Chehab "interface %d unsupported format %u\n", 4250c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber, 4260c0d06caSMauro Carvalho Chehab buffer[2]); 4270c0d06caSMauro Carvalho Chehab return -EINVAL; 4280c0d06caSMauro Carvalho Chehab } 4290c0d06caSMauro Carvalho Chehab 4300c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found format %s.\n", format->name); 4310c0d06caSMauro Carvalho Chehab 4320c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 4330c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 4340c0d06caSMauro Carvalho Chehab 4350c0d06caSMauro Carvalho Chehab /* Parse the frame descriptors. Only uncompressed, MJPEG and frame 4360c0d06caSMauro Carvalho Chehab * based formats have frame descriptors. 4370c0d06caSMauro Carvalho Chehab */ 4380c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 4390c0d06caSMauro Carvalho Chehab buffer[2] == ftype) { 4400c0d06caSMauro Carvalho Chehab frame = &format->frame[format->nframes]; 4410c0d06caSMauro Carvalho Chehab if (ftype != UVC_VS_FRAME_FRAME_BASED) 4420c0d06caSMauro Carvalho Chehab n = buflen > 25 ? buffer[25] : 0; 4430c0d06caSMauro Carvalho Chehab else 4440c0d06caSMauro Carvalho Chehab n = buflen > 21 ? buffer[21] : 0; 4450c0d06caSMauro Carvalho Chehab 4460c0d06caSMauro Carvalho Chehab n = n ? n : 3; 4470c0d06caSMauro Carvalho Chehab 4480c0d06caSMauro Carvalho Chehab if (buflen < 26 + 4*n) { 4490c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 4500c0d06caSMauro Carvalho Chehab "interface %d FRAME error\n", dev->udev->devnum, 4510c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 4520c0d06caSMauro Carvalho Chehab return -EINVAL; 4530c0d06caSMauro Carvalho Chehab } 4540c0d06caSMauro Carvalho Chehab 4550c0d06caSMauro Carvalho Chehab frame->bFrameIndex = buffer[3]; 4560c0d06caSMauro Carvalho Chehab frame->bmCapabilities = buffer[4]; 4570c0d06caSMauro Carvalho Chehab frame->wWidth = get_unaligned_le16(&buffer[5]); 4580c0d06caSMauro Carvalho Chehab frame->wHeight = get_unaligned_le16(&buffer[7]); 4590c0d06caSMauro Carvalho Chehab frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); 4600c0d06caSMauro Carvalho Chehab frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); 4610c0d06caSMauro Carvalho Chehab if (ftype != UVC_VS_FRAME_FRAME_BASED) { 4620c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = 4630c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[17]); 4640c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 4650c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[21]); 4660c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = buffer[25]; 4670c0d06caSMauro Carvalho Chehab } else { 4680c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = 0; 4690c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 4700c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[17]); 4710c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = buffer[21]; 4720c0d06caSMauro Carvalho Chehab } 4730c0d06caSMauro Carvalho Chehab frame->dwFrameInterval = *intervals; 4740c0d06caSMauro Carvalho Chehab 4750c0d06caSMauro Carvalho Chehab /* Several UVC chipsets screw up dwMaxVideoFrameBufferSize 4760c0d06caSMauro Carvalho Chehab * completely. Observed behaviours range from setting the 4770c0d06caSMauro Carvalho Chehab * value to 1.1x the actual frame size to hardwiring the 4780c0d06caSMauro Carvalho Chehab * 16 low bits to 0. This results in a higher than necessary 4790c0d06caSMauro Carvalho Chehab * memory usage as well as a wrong image size information. For 4800c0d06caSMauro Carvalho Chehab * uncompressed formats this can be fixed by computing the 4810c0d06caSMauro Carvalho Chehab * value from the frame size. 4820c0d06caSMauro Carvalho Chehab */ 4830c0d06caSMauro Carvalho Chehab if (!(format->flags & UVC_FMT_FLAG_COMPRESSED)) 4840c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = format->bpp 4850c0d06caSMauro Carvalho Chehab * frame->wWidth * frame->wHeight / 8; 4860c0d06caSMauro Carvalho Chehab 4870c0d06caSMauro Carvalho Chehab /* Some bogus devices report dwMinFrameInterval equal to 4880c0d06caSMauro Carvalho Chehab * dwMaxFrameInterval and have dwFrameIntervalStep set to 4890c0d06caSMauro Carvalho Chehab * zero. Setting all null intervals to 1 fixes the problem and 4900c0d06caSMauro Carvalho Chehab * some other divisions by zero that could happen. 4910c0d06caSMauro Carvalho Chehab */ 4920c0d06caSMauro Carvalho Chehab for (i = 0; i < n; ++i) { 4930c0d06caSMauro Carvalho Chehab interval = get_unaligned_le32(&buffer[26+4*i]); 4940c0d06caSMauro Carvalho Chehab *(*intervals)++ = interval ? interval : 1; 4950c0d06caSMauro Carvalho Chehab } 4960c0d06caSMauro Carvalho Chehab 4970c0d06caSMauro Carvalho Chehab /* Make sure that the default frame interval stays between 4980c0d06caSMauro Carvalho Chehab * the boundaries. 4990c0d06caSMauro Carvalho Chehab */ 5000c0d06caSMauro Carvalho Chehab n -= frame->bFrameIntervalType ? 1 : 2; 5010c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 5020c0d06caSMauro Carvalho Chehab min(frame->dwFrameInterval[n], 5030c0d06caSMauro Carvalho Chehab max(frame->dwFrameInterval[0], 5040c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval)); 5050c0d06caSMauro Carvalho Chehab 5060c0d06caSMauro Carvalho Chehab if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) { 5070c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = 1; 5080c0d06caSMauro Carvalho Chehab frame->dwFrameInterval[0] = 5090c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval; 5100c0d06caSMauro Carvalho Chehab } 5110c0d06caSMauro Carvalho Chehab 5120c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "- %ux%u (%u.%u fps)\n", 5130c0d06caSMauro Carvalho Chehab frame->wWidth, frame->wHeight, 5140c0d06caSMauro Carvalho Chehab 10000000/frame->dwDefaultFrameInterval, 5150c0d06caSMauro Carvalho Chehab (100000000/frame->dwDefaultFrameInterval)%10); 5160c0d06caSMauro Carvalho Chehab 5170c0d06caSMauro Carvalho Chehab format->nframes++; 5180c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 5190c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 5200c0d06caSMauro Carvalho Chehab } 5210c0d06caSMauro Carvalho Chehab 5220c0d06caSMauro Carvalho Chehab if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 5230c0d06caSMauro Carvalho Chehab buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { 5240c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 5250c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 5260c0d06caSMauro Carvalho Chehab } 5270c0d06caSMauro Carvalho Chehab 5280c0d06caSMauro Carvalho Chehab if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 5290c0d06caSMauro Carvalho Chehab buffer[2] == UVC_VS_COLORFORMAT) { 5300c0d06caSMauro Carvalho Chehab if (buflen < 6) { 5310c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 5320c0d06caSMauro Carvalho Chehab "interface %d COLORFORMAT error\n", 5330c0d06caSMauro Carvalho Chehab dev->udev->devnum, 5340c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 5350c0d06caSMauro Carvalho Chehab return -EINVAL; 5360c0d06caSMauro Carvalho Chehab } 5370c0d06caSMauro Carvalho Chehab 5380c0d06caSMauro Carvalho Chehab format->colorspace = uvc_colorspace(buffer[3]); 5390c0d06caSMauro Carvalho Chehab 5400c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 5410c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 5420c0d06caSMauro Carvalho Chehab } 5430c0d06caSMauro Carvalho Chehab 5440c0d06caSMauro Carvalho Chehab return buffer - start; 5450c0d06caSMauro Carvalho Chehab } 5460c0d06caSMauro Carvalho Chehab 5470c0d06caSMauro Carvalho Chehab static int uvc_parse_streaming(struct uvc_device *dev, 5480c0d06caSMauro Carvalho Chehab struct usb_interface *intf) 5490c0d06caSMauro Carvalho Chehab { 5500c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming = NULL; 5510c0d06caSMauro Carvalho Chehab struct uvc_format *format; 5520c0d06caSMauro Carvalho Chehab struct uvc_frame *frame; 5530c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = &intf->altsetting[0]; 5540c0d06caSMauro Carvalho Chehab unsigned char *_buffer, *buffer = alts->extra; 5550c0d06caSMauro Carvalho Chehab int _buflen, buflen = alts->extralen; 5560c0d06caSMauro Carvalho Chehab unsigned int nformats = 0, nframes = 0, nintervals = 0; 5570c0d06caSMauro Carvalho Chehab unsigned int size, i, n, p; 5580c0d06caSMauro Carvalho Chehab __u32 *interval; 5590c0d06caSMauro Carvalho Chehab __u16 psize; 5600c0d06caSMauro Carvalho Chehab int ret = -EINVAL; 5610c0d06caSMauro Carvalho Chehab 5620c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass 5630c0d06caSMauro Carvalho Chehab != UVC_SC_VIDEOSTREAMING) { 5640c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a " 5650c0d06caSMauro Carvalho Chehab "video streaming interface\n", dev->udev->devnum, 5660c0d06caSMauro Carvalho Chehab intf->altsetting[0].desc.bInterfaceNumber); 5670c0d06caSMauro Carvalho Chehab return -EINVAL; 5680c0d06caSMauro Carvalho Chehab } 5690c0d06caSMauro Carvalho Chehab 5700c0d06caSMauro Carvalho Chehab if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) { 5710c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d interface %d is already " 5720c0d06caSMauro Carvalho Chehab "claimed\n", dev->udev->devnum, 5730c0d06caSMauro Carvalho Chehab intf->altsetting[0].desc.bInterfaceNumber); 5740c0d06caSMauro Carvalho Chehab return -EINVAL; 5750c0d06caSMauro Carvalho Chehab } 5760c0d06caSMauro Carvalho Chehab 5770c0d06caSMauro Carvalho Chehab streaming = kzalloc(sizeof *streaming, GFP_KERNEL); 5780c0d06caSMauro Carvalho Chehab if (streaming == NULL) { 5790c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, intf); 5800c0d06caSMauro Carvalho Chehab return -EINVAL; 5810c0d06caSMauro Carvalho Chehab } 5820c0d06caSMauro Carvalho Chehab 5830c0d06caSMauro Carvalho Chehab mutex_init(&streaming->mutex); 5840c0d06caSMauro Carvalho Chehab streaming->dev = dev; 5850c0d06caSMauro Carvalho Chehab streaming->intf = usb_get_intf(intf); 5860c0d06caSMauro Carvalho Chehab streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; 5870c0d06caSMauro Carvalho Chehab 5880c0d06caSMauro Carvalho Chehab /* The Pico iMage webcam has its class-specific interface descriptors 5890c0d06caSMauro Carvalho Chehab * after the endpoint descriptors. 5900c0d06caSMauro Carvalho Chehab */ 5910c0d06caSMauro Carvalho Chehab if (buflen == 0) { 5920c0d06caSMauro Carvalho Chehab for (i = 0; i < alts->desc.bNumEndpoints; ++i) { 5930c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep = &alts->endpoint[i]; 5940c0d06caSMauro Carvalho Chehab 5950c0d06caSMauro Carvalho Chehab if (ep->extralen == 0) 5960c0d06caSMauro Carvalho Chehab continue; 5970c0d06caSMauro Carvalho Chehab 5980c0d06caSMauro Carvalho Chehab if (ep->extralen > 2 && 5990c0d06caSMauro Carvalho Chehab ep->extra[1] == USB_DT_CS_INTERFACE) { 6000c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "trying extra data " 6010c0d06caSMauro Carvalho Chehab "from endpoint %u.\n", i); 6020c0d06caSMauro Carvalho Chehab buffer = alts->endpoint[i].extra; 6030c0d06caSMauro Carvalho Chehab buflen = alts->endpoint[i].extralen; 6040c0d06caSMauro Carvalho Chehab break; 6050c0d06caSMauro Carvalho Chehab } 6060c0d06caSMauro Carvalho Chehab } 6070c0d06caSMauro Carvalho Chehab } 6080c0d06caSMauro Carvalho Chehab 6090c0d06caSMauro Carvalho Chehab /* Skip the standard interface descriptors. */ 6100c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) { 6110c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 6120c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 6130c0d06caSMauro Carvalho Chehab } 6140c0d06caSMauro Carvalho Chehab 6150c0d06caSMauro Carvalho Chehab if (buflen <= 2) { 6160c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "no class-specific streaming " 6170c0d06caSMauro Carvalho Chehab "interface descriptors found.\n"); 6180c0d06caSMauro Carvalho Chehab goto error; 6190c0d06caSMauro Carvalho Chehab } 6200c0d06caSMauro Carvalho Chehab 6210c0d06caSMauro Carvalho Chehab /* Parse the header descriptor. */ 6220c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 6230c0d06caSMauro Carvalho Chehab case UVC_VS_OUTPUT_HEADER: 6240c0d06caSMauro Carvalho Chehab streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 6250c0d06caSMauro Carvalho Chehab size = 9; 6260c0d06caSMauro Carvalho Chehab break; 6270c0d06caSMauro Carvalho Chehab 6280c0d06caSMauro Carvalho Chehab case UVC_VS_INPUT_HEADER: 6290c0d06caSMauro Carvalho Chehab streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 6300c0d06caSMauro Carvalho Chehab size = 13; 6310c0d06caSMauro Carvalho Chehab break; 6320c0d06caSMauro Carvalho Chehab 6330c0d06caSMauro Carvalho Chehab default: 6340c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " 6350c0d06caSMauro Carvalho Chehab "%d HEADER descriptor not found.\n", dev->udev->devnum, 6360c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 6370c0d06caSMauro Carvalho Chehab goto error; 6380c0d06caSMauro Carvalho Chehab } 6390c0d06caSMauro Carvalho Chehab 6400c0d06caSMauro Carvalho Chehab p = buflen >= 4 ? buffer[3] : 0; 6410c0d06caSMauro Carvalho Chehab n = buflen >= size ? buffer[size-1] : 0; 6420c0d06caSMauro Carvalho Chehab 6430c0d06caSMauro Carvalho Chehab if (buflen < size + p*n) { 6440c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 6450c0d06caSMauro Carvalho Chehab "interface %d HEADER descriptor is invalid.\n", 6460c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber); 6470c0d06caSMauro Carvalho Chehab goto error; 6480c0d06caSMauro Carvalho Chehab } 6490c0d06caSMauro Carvalho Chehab 6500c0d06caSMauro Carvalho Chehab streaming->header.bNumFormats = p; 6510c0d06caSMauro Carvalho Chehab streaming->header.bEndpointAddress = buffer[6]; 6520c0d06caSMauro Carvalho Chehab if (buffer[2] == UVC_VS_INPUT_HEADER) { 6530c0d06caSMauro Carvalho Chehab streaming->header.bmInfo = buffer[7]; 6540c0d06caSMauro Carvalho Chehab streaming->header.bTerminalLink = buffer[8]; 6550c0d06caSMauro Carvalho Chehab streaming->header.bStillCaptureMethod = buffer[9]; 6560c0d06caSMauro Carvalho Chehab streaming->header.bTriggerSupport = buffer[10]; 6570c0d06caSMauro Carvalho Chehab streaming->header.bTriggerUsage = buffer[11]; 6580c0d06caSMauro Carvalho Chehab } else { 6590c0d06caSMauro Carvalho Chehab streaming->header.bTerminalLink = buffer[7]; 6600c0d06caSMauro Carvalho Chehab } 6610c0d06caSMauro Carvalho Chehab streaming->header.bControlSize = n; 6620c0d06caSMauro Carvalho Chehab 6630c0d06caSMauro Carvalho Chehab streaming->header.bmaControls = kmemdup(&buffer[size], p * n, 6640c0d06caSMauro Carvalho Chehab GFP_KERNEL); 6650c0d06caSMauro Carvalho Chehab if (streaming->header.bmaControls == NULL) { 6660c0d06caSMauro Carvalho Chehab ret = -ENOMEM; 6670c0d06caSMauro Carvalho Chehab goto error; 6680c0d06caSMauro Carvalho Chehab } 6690c0d06caSMauro Carvalho Chehab 6700c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 6710c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 6720c0d06caSMauro Carvalho Chehab 6730c0d06caSMauro Carvalho Chehab _buffer = buffer; 6740c0d06caSMauro Carvalho Chehab _buflen = buflen; 6750c0d06caSMauro Carvalho Chehab 6760c0d06caSMauro Carvalho Chehab /* Count the format and frame descriptors. */ 6770c0d06caSMauro Carvalho Chehab while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) { 6780c0d06caSMauro Carvalho Chehab switch (_buffer[2]) { 6790c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 6800c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 6810c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 6820c0d06caSMauro Carvalho Chehab nformats++; 6830c0d06caSMauro Carvalho Chehab break; 6840c0d06caSMauro Carvalho Chehab 6850c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 6860c0d06caSMauro Carvalho Chehab /* DV format has no frame descriptor. We will create a 6870c0d06caSMauro Carvalho Chehab * dummy frame descriptor with a dummy frame interval. 6880c0d06caSMauro Carvalho Chehab */ 6890c0d06caSMauro Carvalho Chehab nformats++; 6900c0d06caSMauro Carvalho Chehab nframes++; 6910c0d06caSMauro Carvalho Chehab nintervals++; 6920c0d06caSMauro Carvalho Chehab break; 6930c0d06caSMauro Carvalho Chehab 6940c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MPEG2TS: 6950c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_STREAM_BASED: 6960c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " 6970c0d06caSMauro Carvalho Chehab "interface %d FORMAT %u is not supported.\n", 6980c0d06caSMauro Carvalho Chehab dev->udev->devnum, 6990c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, _buffer[2]); 7000c0d06caSMauro Carvalho Chehab break; 7010c0d06caSMauro Carvalho Chehab 7020c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_UNCOMPRESSED: 7030c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_MJPEG: 7040c0d06caSMauro Carvalho Chehab nframes++; 7050c0d06caSMauro Carvalho Chehab if (_buflen > 25) 7060c0d06caSMauro Carvalho Chehab nintervals += _buffer[25] ? _buffer[25] : 3; 7070c0d06caSMauro Carvalho Chehab break; 7080c0d06caSMauro Carvalho Chehab 7090c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_FRAME_BASED: 7100c0d06caSMauro Carvalho Chehab nframes++; 7110c0d06caSMauro Carvalho Chehab if (_buflen > 21) 7120c0d06caSMauro Carvalho Chehab nintervals += _buffer[21] ? _buffer[21] : 3; 7130c0d06caSMauro Carvalho Chehab break; 7140c0d06caSMauro Carvalho Chehab } 7150c0d06caSMauro Carvalho Chehab 7160c0d06caSMauro Carvalho Chehab _buflen -= _buffer[0]; 7170c0d06caSMauro Carvalho Chehab _buffer += _buffer[0]; 7180c0d06caSMauro Carvalho Chehab } 7190c0d06caSMauro Carvalho Chehab 7200c0d06caSMauro Carvalho Chehab if (nformats == 0) { 7210c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " 7220c0d06caSMauro Carvalho Chehab "%d has no supported formats defined.\n", 7230c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber); 7240c0d06caSMauro Carvalho Chehab goto error; 7250c0d06caSMauro Carvalho Chehab } 7260c0d06caSMauro Carvalho Chehab 7270c0d06caSMauro Carvalho Chehab size = nformats * sizeof *format + nframes * sizeof *frame 7280c0d06caSMauro Carvalho Chehab + nintervals * sizeof *interval; 7290c0d06caSMauro Carvalho Chehab format = kzalloc(size, GFP_KERNEL); 7300c0d06caSMauro Carvalho Chehab if (format == NULL) { 7310c0d06caSMauro Carvalho Chehab ret = -ENOMEM; 7320c0d06caSMauro Carvalho Chehab goto error; 7330c0d06caSMauro Carvalho Chehab } 7340c0d06caSMauro Carvalho Chehab 7350c0d06caSMauro Carvalho Chehab frame = (struct uvc_frame *)&format[nformats]; 7360c0d06caSMauro Carvalho Chehab interval = (__u32 *)&frame[nframes]; 7370c0d06caSMauro Carvalho Chehab 7380c0d06caSMauro Carvalho Chehab streaming->format = format; 7390c0d06caSMauro Carvalho Chehab streaming->nformats = nformats; 7400c0d06caSMauro Carvalho Chehab 7410c0d06caSMauro Carvalho Chehab /* Parse the format descriptors. */ 7420c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) { 7430c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 7440c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 7450c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 7460c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 7470c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 7480c0d06caSMauro Carvalho Chehab format->frame = frame; 7490c0d06caSMauro Carvalho Chehab ret = uvc_parse_format(dev, streaming, format, 7500c0d06caSMauro Carvalho Chehab &interval, buffer, buflen); 7510c0d06caSMauro Carvalho Chehab if (ret < 0) 7520c0d06caSMauro Carvalho Chehab goto error; 7530c0d06caSMauro Carvalho Chehab 7540c0d06caSMauro Carvalho Chehab frame += format->nframes; 7550c0d06caSMauro Carvalho Chehab format++; 7560c0d06caSMauro Carvalho Chehab 7570c0d06caSMauro Carvalho Chehab buflen -= ret; 7580c0d06caSMauro Carvalho Chehab buffer += ret; 7590c0d06caSMauro Carvalho Chehab continue; 7600c0d06caSMauro Carvalho Chehab 7610c0d06caSMauro Carvalho Chehab default: 7620c0d06caSMauro Carvalho Chehab break; 7630c0d06caSMauro Carvalho Chehab } 7640c0d06caSMauro Carvalho Chehab 7650c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 7660c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 7670c0d06caSMauro Carvalho Chehab } 7680c0d06caSMauro Carvalho Chehab 7690c0d06caSMauro Carvalho Chehab if (buflen) 7700c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " 7710c0d06caSMauro Carvalho Chehab "%d has %u bytes of trailing descriptor garbage.\n", 7720c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber, buflen); 7730c0d06caSMauro Carvalho Chehab 7740c0d06caSMauro Carvalho Chehab /* Parse the alternate settings to find the maximum bandwidth. */ 7750c0d06caSMauro Carvalho Chehab for (i = 0; i < intf->num_altsetting; ++i) { 7760c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep; 7770c0d06caSMauro Carvalho Chehab alts = &intf->altsetting[i]; 7780c0d06caSMauro Carvalho Chehab ep = uvc_find_endpoint(alts, 7790c0d06caSMauro Carvalho Chehab streaming->header.bEndpointAddress); 7800c0d06caSMauro Carvalho Chehab if (ep == NULL) 7810c0d06caSMauro Carvalho Chehab continue; 7820c0d06caSMauro Carvalho Chehab 7830c0d06caSMauro Carvalho Chehab psize = le16_to_cpu(ep->desc.wMaxPacketSize); 7840c0d06caSMauro Carvalho Chehab psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); 7850c0d06caSMauro Carvalho Chehab if (psize > streaming->maxpsize) 7860c0d06caSMauro Carvalho Chehab streaming->maxpsize = psize; 7870c0d06caSMauro Carvalho Chehab } 7880c0d06caSMauro Carvalho Chehab 7890c0d06caSMauro Carvalho Chehab list_add_tail(&streaming->list, &dev->streams); 7900c0d06caSMauro Carvalho Chehab return 0; 7910c0d06caSMauro Carvalho Chehab 7920c0d06caSMauro Carvalho Chehab error: 7930c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, intf); 7940c0d06caSMauro Carvalho Chehab usb_put_intf(intf); 7950c0d06caSMauro Carvalho Chehab kfree(streaming->format); 7960c0d06caSMauro Carvalho Chehab kfree(streaming->header.bmaControls); 7970c0d06caSMauro Carvalho Chehab kfree(streaming); 7980c0d06caSMauro Carvalho Chehab return ret; 7990c0d06caSMauro Carvalho Chehab } 8000c0d06caSMauro Carvalho Chehab 8010c0d06caSMauro Carvalho Chehab static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, 8020c0d06caSMauro Carvalho Chehab unsigned int num_pads, unsigned int extra_size) 8030c0d06caSMauro Carvalho Chehab { 8040c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 8050c0d06caSMauro Carvalho Chehab unsigned int num_inputs; 8060c0d06caSMauro Carvalho Chehab unsigned int size; 8070c0d06caSMauro Carvalho Chehab unsigned int i; 8080c0d06caSMauro Carvalho Chehab 8090c0d06caSMauro Carvalho Chehab extra_size = ALIGN(extra_size, sizeof(*entity->pads)); 8100c0d06caSMauro Carvalho Chehab num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1; 8110c0d06caSMauro Carvalho Chehab size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads 8120c0d06caSMauro Carvalho Chehab + num_inputs; 8130c0d06caSMauro Carvalho Chehab entity = kzalloc(size, GFP_KERNEL); 8140c0d06caSMauro Carvalho Chehab if (entity == NULL) 8150c0d06caSMauro Carvalho Chehab return NULL; 8160c0d06caSMauro Carvalho Chehab 8170c0d06caSMauro Carvalho Chehab entity->id = id; 8180c0d06caSMauro Carvalho Chehab entity->type = type; 8190c0d06caSMauro Carvalho Chehab 8200c0d06caSMauro Carvalho Chehab entity->num_links = 0; 8210c0d06caSMauro Carvalho Chehab entity->num_pads = num_pads; 8220c0d06caSMauro Carvalho Chehab entity->pads = ((void *)(entity + 1)) + extra_size; 8230c0d06caSMauro Carvalho Chehab 8240c0d06caSMauro Carvalho Chehab for (i = 0; i < num_inputs; ++i) 8250c0d06caSMauro Carvalho Chehab entity->pads[i].flags = MEDIA_PAD_FL_SINK; 8260c0d06caSMauro Carvalho Chehab if (!UVC_ENTITY_IS_OTERM(entity)) 8270c0d06caSMauro Carvalho Chehab entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE; 8280c0d06caSMauro Carvalho Chehab 8290c0d06caSMauro Carvalho Chehab entity->bNrInPins = num_inputs; 8300c0d06caSMauro Carvalho Chehab entity->baSourceID = (__u8 *)(&entity->pads[num_pads]); 8310c0d06caSMauro Carvalho Chehab 8320c0d06caSMauro Carvalho Chehab return entity; 8330c0d06caSMauro Carvalho Chehab } 8340c0d06caSMauro Carvalho Chehab 8350c0d06caSMauro Carvalho Chehab /* Parse vendor-specific extensions. */ 8360c0d06caSMauro Carvalho Chehab static int uvc_parse_vendor_control(struct uvc_device *dev, 8370c0d06caSMauro Carvalho Chehab const unsigned char *buffer, int buflen) 8380c0d06caSMauro Carvalho Chehab { 8390c0d06caSMauro Carvalho Chehab struct usb_device *udev = dev->udev; 8400c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 8410c0d06caSMauro Carvalho Chehab struct uvc_entity *unit; 8420c0d06caSMauro Carvalho Chehab unsigned int n, p; 8430c0d06caSMauro Carvalho Chehab int handled = 0; 8440c0d06caSMauro Carvalho Chehab 8450c0d06caSMauro Carvalho Chehab switch (le16_to_cpu(dev->udev->descriptor.idVendor)) { 8460c0d06caSMauro Carvalho Chehab case 0x046d: /* Logitech */ 8470c0d06caSMauro Carvalho Chehab if (buffer[1] != 0x41 || buffer[2] != 0x01) 8480c0d06caSMauro Carvalho Chehab break; 8490c0d06caSMauro Carvalho Chehab 8500c0d06caSMauro Carvalho Chehab /* Logitech implements several vendor specific functions 8510c0d06caSMauro Carvalho Chehab * through vendor specific extension units (LXU). 8520c0d06caSMauro Carvalho Chehab * 8530c0d06caSMauro Carvalho Chehab * The LXU descriptors are similar to XU descriptors 8540c0d06caSMauro Carvalho Chehab * (see "USB Device Video Class for Video Devices", section 8550c0d06caSMauro Carvalho Chehab * 3.7.2.6 "Extension Unit Descriptor") with the following 8560c0d06caSMauro Carvalho Chehab * differences: 8570c0d06caSMauro Carvalho Chehab * 8580c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 8590c0d06caSMauro Carvalho Chehab * 0 bLength 1 Number 8600c0d06caSMauro Carvalho Chehab * Size of this descriptor, in bytes: 24+p+n*2 8610c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 8620c0d06caSMauro Carvalho Chehab * 23+p+n bmControlsType N Bitmap 8630c0d06caSMauro Carvalho Chehab * Individual bits in the set are defined: 8640c0d06caSMauro Carvalho Chehab * 0: Absolute 8650c0d06caSMauro Carvalho Chehab * 1: Relative 8660c0d06caSMauro Carvalho Chehab * 8670c0d06caSMauro Carvalho Chehab * This bitset is mapped exactly the same as bmControls. 8680c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 8690c0d06caSMauro Carvalho Chehab * 23+p+n*2 bReserved 1 Boolean 8700c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 8710c0d06caSMauro Carvalho Chehab * 24+p+n*2 iExtension 1 Index 8720c0d06caSMauro Carvalho Chehab * Index of a string descriptor that describes this 8730c0d06caSMauro Carvalho Chehab * extension unit. 8740c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 8750c0d06caSMauro Carvalho Chehab */ 8760c0d06caSMauro Carvalho Chehab p = buflen >= 22 ? buffer[21] : 0; 8770c0d06caSMauro Carvalho Chehab n = buflen >= 25 + p ? buffer[22+p] : 0; 8780c0d06caSMauro Carvalho Chehab 8790c0d06caSMauro Carvalho Chehab if (buflen < 25 + p + 2*n) { 8800c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 8810c0d06caSMauro Carvalho Chehab "interface %d EXTENSION_UNIT error\n", 8820c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 8830c0d06caSMauro Carvalho Chehab break; 8840c0d06caSMauro Carvalho Chehab } 8850c0d06caSMauro Carvalho Chehab 8860c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3], 8870c0d06caSMauro Carvalho Chehab p + 1, 2*n); 8880c0d06caSMauro Carvalho Chehab if (unit == NULL) 8890c0d06caSMauro Carvalho Chehab return -ENOMEM; 8900c0d06caSMauro Carvalho Chehab 8910c0d06caSMauro Carvalho Chehab memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); 8920c0d06caSMauro Carvalho Chehab unit->extension.bNumControls = buffer[20]; 8930c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[22], p); 8940c0d06caSMauro Carvalho Chehab unit->extension.bControlSize = buffer[22+p]; 8950c0d06caSMauro Carvalho Chehab unit->extension.bmControls = (__u8 *)unit + sizeof(*unit); 8960c0d06caSMauro Carvalho Chehab unit->extension.bmControlsType = (__u8 *)unit + sizeof(*unit) 8970c0d06caSMauro Carvalho Chehab + n; 8980c0d06caSMauro Carvalho Chehab memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); 8990c0d06caSMauro Carvalho Chehab 9000c0d06caSMauro Carvalho Chehab if (buffer[24+p+2*n] != 0) 9010c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[24+p+2*n], unit->name, 9020c0d06caSMauro Carvalho Chehab sizeof unit->name); 9030c0d06caSMauro Carvalho Chehab else 9040c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Extension %u", buffer[3]); 9050c0d06caSMauro Carvalho Chehab 9060c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 9070c0d06caSMauro Carvalho Chehab handled = 1; 9080c0d06caSMauro Carvalho Chehab break; 9090c0d06caSMauro Carvalho Chehab } 9100c0d06caSMauro Carvalho Chehab 9110c0d06caSMauro Carvalho Chehab return handled; 9120c0d06caSMauro Carvalho Chehab } 9130c0d06caSMauro Carvalho Chehab 9140c0d06caSMauro Carvalho Chehab static int uvc_parse_standard_control(struct uvc_device *dev, 9150c0d06caSMauro Carvalho Chehab const unsigned char *buffer, int buflen) 9160c0d06caSMauro Carvalho Chehab { 9170c0d06caSMauro Carvalho Chehab struct usb_device *udev = dev->udev; 9180c0d06caSMauro Carvalho Chehab struct uvc_entity *unit, *term; 9190c0d06caSMauro Carvalho Chehab struct usb_interface *intf; 9200c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 9210c0d06caSMauro Carvalho Chehab unsigned int i, n, p, len; 9220c0d06caSMauro Carvalho Chehab __u16 type; 9230c0d06caSMauro Carvalho Chehab 9240c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 9250c0d06caSMauro Carvalho Chehab case UVC_VC_HEADER: 9260c0d06caSMauro Carvalho Chehab n = buflen >= 12 ? buffer[11] : 0; 9270c0d06caSMauro Carvalho Chehab 9280c0d06caSMauro Carvalho Chehab if (buflen < 12 || buflen < 12 + n) { 9290c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 9300c0d06caSMauro Carvalho Chehab "interface %d HEADER error\n", udev->devnum, 9310c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 9320c0d06caSMauro Carvalho Chehab return -EINVAL; 9330c0d06caSMauro Carvalho Chehab } 9340c0d06caSMauro Carvalho Chehab 9350c0d06caSMauro Carvalho Chehab dev->uvc_version = get_unaligned_le16(&buffer[3]); 9360c0d06caSMauro Carvalho Chehab dev->clock_frequency = get_unaligned_le32(&buffer[7]); 9370c0d06caSMauro Carvalho Chehab 9380c0d06caSMauro Carvalho Chehab /* Parse all USB Video Streaming interfaces. */ 9390c0d06caSMauro Carvalho Chehab for (i = 0; i < n; ++i) { 9400c0d06caSMauro Carvalho Chehab intf = usb_ifnum_to_if(udev, buffer[12+i]); 9410c0d06caSMauro Carvalho Chehab if (intf == NULL) { 9420c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d " 9430c0d06caSMauro Carvalho Chehab "interface %d doesn't exists\n", 9440c0d06caSMauro Carvalho Chehab udev->devnum, i); 9450c0d06caSMauro Carvalho Chehab continue; 9460c0d06caSMauro Carvalho Chehab } 9470c0d06caSMauro Carvalho Chehab 9480c0d06caSMauro Carvalho Chehab uvc_parse_streaming(dev, intf); 9490c0d06caSMauro Carvalho Chehab } 9500c0d06caSMauro Carvalho Chehab break; 9510c0d06caSMauro Carvalho Chehab 9520c0d06caSMauro Carvalho Chehab case UVC_VC_INPUT_TERMINAL: 9530c0d06caSMauro Carvalho Chehab if (buflen < 8) { 9540c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 9550c0d06caSMauro Carvalho Chehab "interface %d INPUT_TERMINAL error\n", 9560c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 9570c0d06caSMauro Carvalho Chehab return -EINVAL; 9580c0d06caSMauro Carvalho Chehab } 9590c0d06caSMauro Carvalho Chehab 9600c0d06caSMauro Carvalho Chehab /* Make sure the terminal type MSB is not null, otherwise it 9610c0d06caSMauro Carvalho Chehab * could be confused with a unit. 9620c0d06caSMauro Carvalho Chehab */ 9630c0d06caSMauro Carvalho Chehab type = get_unaligned_le16(&buffer[4]); 9640c0d06caSMauro Carvalho Chehab if ((type & 0xff00) == 0) { 9650c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 9660c0d06caSMauro Carvalho Chehab "interface %d INPUT_TERMINAL %d has invalid " 9670c0d06caSMauro Carvalho Chehab "type 0x%04x, skipping\n", udev->devnum, 9680c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, 9690c0d06caSMauro Carvalho Chehab buffer[3], type); 9700c0d06caSMauro Carvalho Chehab return 0; 9710c0d06caSMauro Carvalho Chehab } 9720c0d06caSMauro Carvalho Chehab 9730c0d06caSMauro Carvalho Chehab n = 0; 9740c0d06caSMauro Carvalho Chehab p = 0; 9750c0d06caSMauro Carvalho Chehab len = 8; 9760c0d06caSMauro Carvalho Chehab 9770c0d06caSMauro Carvalho Chehab if (type == UVC_ITT_CAMERA) { 9780c0d06caSMauro Carvalho Chehab n = buflen >= 15 ? buffer[14] : 0; 9790c0d06caSMauro Carvalho Chehab len = 15; 9800c0d06caSMauro Carvalho Chehab 9810c0d06caSMauro Carvalho Chehab } else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) { 9820c0d06caSMauro Carvalho Chehab n = buflen >= 9 ? buffer[8] : 0; 9830c0d06caSMauro Carvalho Chehab p = buflen >= 10 + n ? buffer[9+n] : 0; 9840c0d06caSMauro Carvalho Chehab len = 10; 9850c0d06caSMauro Carvalho Chehab } 9860c0d06caSMauro Carvalho Chehab 9870c0d06caSMauro Carvalho Chehab if (buflen < len + n + p) { 9880c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 9890c0d06caSMauro Carvalho Chehab "interface %d INPUT_TERMINAL error\n", 9900c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 9910c0d06caSMauro Carvalho Chehab return -EINVAL; 9920c0d06caSMauro Carvalho Chehab } 9930c0d06caSMauro Carvalho Chehab 9940c0d06caSMauro Carvalho Chehab term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3], 9950c0d06caSMauro Carvalho Chehab 1, n + p); 9960c0d06caSMauro Carvalho Chehab if (term == NULL) 9970c0d06caSMauro Carvalho Chehab return -ENOMEM; 9980c0d06caSMauro Carvalho Chehab 9990c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) { 10000c0d06caSMauro Carvalho Chehab term->camera.bControlSize = n; 10010c0d06caSMauro Carvalho Chehab term->camera.bmControls = (__u8 *)term + sizeof *term; 10020c0d06caSMauro Carvalho Chehab term->camera.wObjectiveFocalLengthMin = 10030c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[8]); 10040c0d06caSMauro Carvalho Chehab term->camera.wObjectiveFocalLengthMax = 10050c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[10]); 10060c0d06caSMauro Carvalho Chehab term->camera.wOcularFocalLength = 10070c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[12]); 10080c0d06caSMauro Carvalho Chehab memcpy(term->camera.bmControls, &buffer[15], n); 10090c0d06caSMauro Carvalho Chehab } else if (UVC_ENTITY_TYPE(term) == 10100c0d06caSMauro Carvalho Chehab UVC_ITT_MEDIA_TRANSPORT_INPUT) { 10110c0d06caSMauro Carvalho Chehab term->media.bControlSize = n; 10120c0d06caSMauro Carvalho Chehab term->media.bmControls = (__u8 *)term + sizeof *term; 10130c0d06caSMauro Carvalho Chehab term->media.bTransportModeSize = p; 10140c0d06caSMauro Carvalho Chehab term->media.bmTransportModes = (__u8 *)term 10150c0d06caSMauro Carvalho Chehab + sizeof *term + n; 10160c0d06caSMauro Carvalho Chehab memcpy(term->media.bmControls, &buffer[9], n); 10170c0d06caSMauro Carvalho Chehab memcpy(term->media.bmTransportModes, &buffer[10+n], p); 10180c0d06caSMauro Carvalho Chehab } 10190c0d06caSMauro Carvalho Chehab 10200c0d06caSMauro Carvalho Chehab if (buffer[7] != 0) 10210c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[7], term->name, 10220c0d06caSMauro Carvalho Chehab sizeof term->name); 10230c0d06caSMauro Carvalho Chehab else if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) 10240c0d06caSMauro Carvalho Chehab sprintf(term->name, "Camera %u", buffer[3]); 10250c0d06caSMauro Carvalho Chehab else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT) 10260c0d06caSMauro Carvalho Chehab sprintf(term->name, "Media %u", buffer[3]); 10270c0d06caSMauro Carvalho Chehab else 10280c0d06caSMauro Carvalho Chehab sprintf(term->name, "Input %u", buffer[3]); 10290c0d06caSMauro Carvalho Chehab 10300c0d06caSMauro Carvalho Chehab list_add_tail(&term->list, &dev->entities); 10310c0d06caSMauro Carvalho Chehab break; 10320c0d06caSMauro Carvalho Chehab 10330c0d06caSMauro Carvalho Chehab case UVC_VC_OUTPUT_TERMINAL: 10340c0d06caSMauro Carvalho Chehab if (buflen < 9) { 10350c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 10360c0d06caSMauro Carvalho Chehab "interface %d OUTPUT_TERMINAL error\n", 10370c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 10380c0d06caSMauro Carvalho Chehab return -EINVAL; 10390c0d06caSMauro Carvalho Chehab } 10400c0d06caSMauro Carvalho Chehab 10410c0d06caSMauro Carvalho Chehab /* Make sure the terminal type MSB is not null, otherwise it 10420c0d06caSMauro Carvalho Chehab * could be confused with a unit. 10430c0d06caSMauro Carvalho Chehab */ 10440c0d06caSMauro Carvalho Chehab type = get_unaligned_le16(&buffer[4]); 10450c0d06caSMauro Carvalho Chehab if ((type & 0xff00) == 0) { 10460c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 10470c0d06caSMauro Carvalho Chehab "interface %d OUTPUT_TERMINAL %d has invalid " 10480c0d06caSMauro Carvalho Chehab "type 0x%04x, skipping\n", udev->devnum, 10490c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, buffer[3], type); 10500c0d06caSMauro Carvalho Chehab return 0; 10510c0d06caSMauro Carvalho Chehab } 10520c0d06caSMauro Carvalho Chehab 10530c0d06caSMauro Carvalho Chehab term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3], 10540c0d06caSMauro Carvalho Chehab 1, 0); 10550c0d06caSMauro Carvalho Chehab if (term == NULL) 10560c0d06caSMauro Carvalho Chehab return -ENOMEM; 10570c0d06caSMauro Carvalho Chehab 10580c0d06caSMauro Carvalho Chehab memcpy(term->baSourceID, &buffer[7], 1); 10590c0d06caSMauro Carvalho Chehab 10600c0d06caSMauro Carvalho Chehab if (buffer[8] != 0) 10610c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[8], term->name, 10620c0d06caSMauro Carvalho Chehab sizeof term->name); 10630c0d06caSMauro Carvalho Chehab else 10640c0d06caSMauro Carvalho Chehab sprintf(term->name, "Output %u", buffer[3]); 10650c0d06caSMauro Carvalho Chehab 10660c0d06caSMauro Carvalho Chehab list_add_tail(&term->list, &dev->entities); 10670c0d06caSMauro Carvalho Chehab break; 10680c0d06caSMauro Carvalho Chehab 10690c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 10700c0d06caSMauro Carvalho Chehab p = buflen >= 5 ? buffer[4] : 0; 10710c0d06caSMauro Carvalho Chehab 10720c0d06caSMauro Carvalho Chehab if (buflen < 5 || buflen < 6 + p) { 10730c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 10740c0d06caSMauro Carvalho Chehab "interface %d SELECTOR_UNIT error\n", 10750c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 10760c0d06caSMauro Carvalho Chehab return -EINVAL; 10770c0d06caSMauro Carvalho Chehab } 10780c0d06caSMauro Carvalho Chehab 10790c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0); 10800c0d06caSMauro Carvalho Chehab if (unit == NULL) 10810c0d06caSMauro Carvalho Chehab return -ENOMEM; 10820c0d06caSMauro Carvalho Chehab 10830c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[5], p); 10840c0d06caSMauro Carvalho Chehab 10850c0d06caSMauro Carvalho Chehab if (buffer[5+p] != 0) 10860c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[5+p], unit->name, 10870c0d06caSMauro Carvalho Chehab sizeof unit->name); 10880c0d06caSMauro Carvalho Chehab else 10890c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Selector %u", buffer[3]); 10900c0d06caSMauro Carvalho Chehab 10910c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 10920c0d06caSMauro Carvalho Chehab break; 10930c0d06caSMauro Carvalho Chehab 10940c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 10950c0d06caSMauro Carvalho Chehab n = buflen >= 8 ? buffer[7] : 0; 10960c0d06caSMauro Carvalho Chehab p = dev->uvc_version >= 0x0110 ? 10 : 9; 10970c0d06caSMauro Carvalho Chehab 10980c0d06caSMauro Carvalho Chehab if (buflen < p + n) { 10990c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 11000c0d06caSMauro Carvalho Chehab "interface %d PROCESSING_UNIT error\n", 11010c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 11020c0d06caSMauro Carvalho Chehab return -EINVAL; 11030c0d06caSMauro Carvalho Chehab } 11040c0d06caSMauro Carvalho Chehab 11050c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n); 11060c0d06caSMauro Carvalho Chehab if (unit == NULL) 11070c0d06caSMauro Carvalho Chehab return -ENOMEM; 11080c0d06caSMauro Carvalho Chehab 11090c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[4], 1); 11100c0d06caSMauro Carvalho Chehab unit->processing.wMaxMultiplier = 11110c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[5]); 11120c0d06caSMauro Carvalho Chehab unit->processing.bControlSize = buffer[7]; 11130c0d06caSMauro Carvalho Chehab unit->processing.bmControls = (__u8 *)unit + sizeof *unit; 11140c0d06caSMauro Carvalho Chehab memcpy(unit->processing.bmControls, &buffer[8], n); 11150c0d06caSMauro Carvalho Chehab if (dev->uvc_version >= 0x0110) 11160c0d06caSMauro Carvalho Chehab unit->processing.bmVideoStandards = buffer[9+n]; 11170c0d06caSMauro Carvalho Chehab 11180c0d06caSMauro Carvalho Chehab if (buffer[8+n] != 0) 11190c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[8+n], unit->name, 11200c0d06caSMauro Carvalho Chehab sizeof unit->name); 11210c0d06caSMauro Carvalho Chehab else 11220c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Processing %u", buffer[3]); 11230c0d06caSMauro Carvalho Chehab 11240c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 11250c0d06caSMauro Carvalho Chehab break; 11260c0d06caSMauro Carvalho Chehab 11270c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 11280c0d06caSMauro Carvalho Chehab p = buflen >= 22 ? buffer[21] : 0; 11290c0d06caSMauro Carvalho Chehab n = buflen >= 24 + p ? buffer[22+p] : 0; 11300c0d06caSMauro Carvalho Chehab 11310c0d06caSMauro Carvalho Chehab if (buflen < 24 + p + n) { 11320c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " 11330c0d06caSMauro Carvalho Chehab "interface %d EXTENSION_UNIT error\n", 11340c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 11350c0d06caSMauro Carvalho Chehab return -EINVAL; 11360c0d06caSMauro Carvalho Chehab } 11370c0d06caSMauro Carvalho Chehab 11380c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n); 11390c0d06caSMauro Carvalho Chehab if (unit == NULL) 11400c0d06caSMauro Carvalho Chehab return -ENOMEM; 11410c0d06caSMauro Carvalho Chehab 11420c0d06caSMauro Carvalho Chehab memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); 11430c0d06caSMauro Carvalho Chehab unit->extension.bNumControls = buffer[20]; 11440c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[22], p); 11450c0d06caSMauro Carvalho Chehab unit->extension.bControlSize = buffer[22+p]; 11460c0d06caSMauro Carvalho Chehab unit->extension.bmControls = (__u8 *)unit + sizeof *unit; 11470c0d06caSMauro Carvalho Chehab memcpy(unit->extension.bmControls, &buffer[23+p], n); 11480c0d06caSMauro Carvalho Chehab 11490c0d06caSMauro Carvalho Chehab if (buffer[23+p+n] != 0) 11500c0d06caSMauro Carvalho Chehab usb_string(udev, buffer[23+p+n], unit->name, 11510c0d06caSMauro Carvalho Chehab sizeof unit->name); 11520c0d06caSMauro Carvalho Chehab else 11530c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Extension %u", buffer[3]); 11540c0d06caSMauro Carvalho Chehab 11550c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 11560c0d06caSMauro Carvalho Chehab break; 11570c0d06caSMauro Carvalho Chehab 11580c0d06caSMauro Carvalho Chehab default: 11590c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found an unknown CS_INTERFACE " 11600c0d06caSMauro Carvalho Chehab "descriptor (%u)\n", buffer[2]); 11610c0d06caSMauro Carvalho Chehab break; 11620c0d06caSMauro Carvalho Chehab } 11630c0d06caSMauro Carvalho Chehab 11640c0d06caSMauro Carvalho Chehab return 0; 11650c0d06caSMauro Carvalho Chehab } 11660c0d06caSMauro Carvalho Chehab 11670c0d06caSMauro Carvalho Chehab static int uvc_parse_control(struct uvc_device *dev) 11680c0d06caSMauro Carvalho Chehab { 11690c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 11700c0d06caSMauro Carvalho Chehab unsigned char *buffer = alts->extra; 11710c0d06caSMauro Carvalho Chehab int buflen = alts->extralen; 11720c0d06caSMauro Carvalho Chehab int ret; 11730c0d06caSMauro Carvalho Chehab 11740c0d06caSMauro Carvalho Chehab /* Parse the default alternate setting only, as the UVC specification 11750c0d06caSMauro Carvalho Chehab * defines a single alternate setting, the default alternate setting 11760c0d06caSMauro Carvalho Chehab * zero. 11770c0d06caSMauro Carvalho Chehab */ 11780c0d06caSMauro Carvalho Chehab 11790c0d06caSMauro Carvalho Chehab while (buflen > 2) { 11800c0d06caSMauro Carvalho Chehab if (uvc_parse_vendor_control(dev, buffer, buflen) || 11810c0d06caSMauro Carvalho Chehab buffer[1] != USB_DT_CS_INTERFACE) 11820c0d06caSMauro Carvalho Chehab goto next_descriptor; 11830c0d06caSMauro Carvalho Chehab 11840c0d06caSMauro Carvalho Chehab if ((ret = uvc_parse_standard_control(dev, buffer, buflen)) < 0) 11850c0d06caSMauro Carvalho Chehab return ret; 11860c0d06caSMauro Carvalho Chehab 11870c0d06caSMauro Carvalho Chehab next_descriptor: 11880c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 11890c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 11900c0d06caSMauro Carvalho Chehab } 11910c0d06caSMauro Carvalho Chehab 11920c0d06caSMauro Carvalho Chehab /* Check if the optional status endpoint is present. Built-in iSight 11930c0d06caSMauro Carvalho Chehab * webcams have an interrupt endpoint but spit proprietary data that 11940c0d06caSMauro Carvalho Chehab * don't conform to the UVC status endpoint messages. Don't try to 11950c0d06caSMauro Carvalho Chehab * handle the interrupt endpoint for those cameras. 11960c0d06caSMauro Carvalho Chehab */ 11970c0d06caSMauro Carvalho Chehab if (alts->desc.bNumEndpoints == 1 && 11980c0d06caSMauro Carvalho Chehab !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) { 11990c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep = &alts->endpoint[0]; 12000c0d06caSMauro Carvalho Chehab struct usb_endpoint_descriptor *desc = &ep->desc; 12010c0d06caSMauro Carvalho Chehab 12020c0d06caSMauro Carvalho Chehab if (usb_endpoint_is_int_in(desc) && 12030c0d06caSMauro Carvalho Chehab le16_to_cpu(desc->wMaxPacketSize) >= 8 && 12040c0d06caSMauro Carvalho Chehab desc->bInterval != 0) { 12050c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found a Status endpoint " 12060c0d06caSMauro Carvalho Chehab "(addr %02x).\n", desc->bEndpointAddress); 12070c0d06caSMauro Carvalho Chehab dev->int_ep = ep; 12080c0d06caSMauro Carvalho Chehab } 12090c0d06caSMauro Carvalho Chehab } 12100c0d06caSMauro Carvalho Chehab 12110c0d06caSMauro Carvalho Chehab return 0; 12120c0d06caSMauro Carvalho Chehab } 12130c0d06caSMauro Carvalho Chehab 12140c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 12150c0d06caSMauro Carvalho Chehab * UVC device scan 12160c0d06caSMauro Carvalho Chehab */ 12170c0d06caSMauro Carvalho Chehab 12180c0d06caSMauro Carvalho Chehab /* 12190c0d06caSMauro Carvalho Chehab * Scan the UVC descriptors to locate a chain starting at an Output Terminal 12200c0d06caSMauro Carvalho Chehab * and containing the following units: 12210c0d06caSMauro Carvalho Chehab * 12220c0d06caSMauro Carvalho Chehab * - one or more Output Terminals (USB Streaming or Display) 12230c0d06caSMauro Carvalho Chehab * - zero or one Processing Unit 12240c0d06caSMauro Carvalho Chehab * - zero, one or more single-input Selector Units 12250c0d06caSMauro Carvalho Chehab * - zero or one multiple-input Selector Units, provided all inputs are 12260c0d06caSMauro Carvalho Chehab * connected to input terminals 12270c0d06caSMauro Carvalho Chehab * - zero, one or mode single-input Extension Units 12280c0d06caSMauro Carvalho Chehab * - one or more Input Terminals (Camera, External or USB Streaming) 12290c0d06caSMauro Carvalho Chehab * 12300c0d06caSMauro Carvalho Chehab * The terminal and units must match on of the following structures: 12310c0d06caSMauro Carvalho Chehab * 12320c0d06caSMauro Carvalho Chehab * ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0) 12330c0d06caSMauro Carvalho Chehab * ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ... 12340c0d06caSMauro Carvalho Chehab * ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n) 12350c0d06caSMauro Carvalho Chehab * 12360c0d06caSMauro Carvalho Chehab * +---------+ +---------+ -> OTT_*(0) 12370c0d06caSMauro Carvalho Chehab * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ... 12380c0d06caSMauro Carvalho Chehab * +---------+ +---------+ -> OTT_*(n) 12390c0d06caSMauro Carvalho Chehab * 12400c0d06caSMauro Carvalho Chehab * The Processing Unit and Extension Units can be in any order. Additional 12410c0d06caSMauro Carvalho Chehab * Extension Units connected to the main chain as single-unit branches are 12420c0d06caSMauro Carvalho Chehab * also supported. Single-input Selector Units are ignored. 12430c0d06caSMauro Carvalho Chehab */ 12440c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_entity(struct uvc_video_chain *chain, 12450c0d06caSMauro Carvalho Chehab struct uvc_entity *entity) 12460c0d06caSMauro Carvalho Chehab { 12470c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(entity)) { 12480c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 12490c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 12500c0d06caSMauro Carvalho Chehab printk(" <- XU %d", entity->id); 12510c0d06caSMauro Carvalho Chehab 12520c0d06caSMauro Carvalho Chehab if (entity->bNrInPins != 1) { 12530c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more " 12540c0d06caSMauro Carvalho Chehab "than 1 input pin.\n", entity->id); 12550c0d06caSMauro Carvalho Chehab return -1; 12560c0d06caSMauro Carvalho Chehab } 12570c0d06caSMauro Carvalho Chehab 12580c0d06caSMauro Carvalho Chehab break; 12590c0d06caSMauro Carvalho Chehab 12600c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 12610c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 12620c0d06caSMauro Carvalho Chehab printk(" <- PU %d", entity->id); 12630c0d06caSMauro Carvalho Chehab 12640c0d06caSMauro Carvalho Chehab if (chain->processing != NULL) { 12650c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found multiple " 12660c0d06caSMauro Carvalho Chehab "Processing Units in chain.\n"); 12670c0d06caSMauro Carvalho Chehab return -1; 12680c0d06caSMauro Carvalho Chehab } 12690c0d06caSMauro Carvalho Chehab 12700c0d06caSMauro Carvalho Chehab chain->processing = entity; 12710c0d06caSMauro Carvalho Chehab break; 12720c0d06caSMauro Carvalho Chehab 12730c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 12740c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 12750c0d06caSMauro Carvalho Chehab printk(" <- SU %d", entity->id); 12760c0d06caSMauro Carvalho Chehab 12770c0d06caSMauro Carvalho Chehab /* Single-input selector units are ignored. */ 12780c0d06caSMauro Carvalho Chehab if (entity->bNrInPins == 1) 12790c0d06caSMauro Carvalho Chehab break; 12800c0d06caSMauro Carvalho Chehab 12810c0d06caSMauro Carvalho Chehab if (chain->selector != NULL) { 12820c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector " 12830c0d06caSMauro Carvalho Chehab "Units in chain.\n"); 12840c0d06caSMauro Carvalho Chehab return -1; 12850c0d06caSMauro Carvalho Chehab } 12860c0d06caSMauro Carvalho Chehab 12870c0d06caSMauro Carvalho Chehab chain->selector = entity; 12880c0d06caSMauro Carvalho Chehab break; 12890c0d06caSMauro Carvalho Chehab 12900c0d06caSMauro Carvalho Chehab case UVC_ITT_VENDOR_SPECIFIC: 12910c0d06caSMauro Carvalho Chehab case UVC_ITT_CAMERA: 12920c0d06caSMauro Carvalho Chehab case UVC_ITT_MEDIA_TRANSPORT_INPUT: 12930c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 12940c0d06caSMauro Carvalho Chehab printk(" <- IT %d\n", entity->id); 12950c0d06caSMauro Carvalho Chehab 12960c0d06caSMauro Carvalho Chehab break; 12970c0d06caSMauro Carvalho Chehab 12980c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 12990c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 13000c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 13010c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 13020c0d06caSMauro Carvalho Chehab printk(" OT %d", entity->id); 13030c0d06caSMauro Carvalho Chehab 13040c0d06caSMauro Carvalho Chehab break; 13050c0d06caSMauro Carvalho Chehab 13060c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 13070c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_IS_ITERM(entity)) { 13080c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 13090c0d06caSMauro Carvalho Chehab printk(" <- IT %d\n", entity->id); 13100c0d06caSMauro Carvalho Chehab } else { 13110c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 13120c0d06caSMauro Carvalho Chehab printk(" OT %d", entity->id); 13130c0d06caSMauro Carvalho Chehab } 13140c0d06caSMauro Carvalho Chehab 13150c0d06caSMauro Carvalho Chehab break; 13160c0d06caSMauro Carvalho Chehab 13170c0d06caSMauro Carvalho Chehab default: 13180c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type " 13190c0d06caSMauro Carvalho Chehab "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity)); 13200c0d06caSMauro Carvalho Chehab return -1; 13210c0d06caSMauro Carvalho Chehab } 13220c0d06caSMauro Carvalho Chehab 13230c0d06caSMauro Carvalho Chehab list_add_tail(&entity->chain, &chain->entities); 13240c0d06caSMauro Carvalho Chehab return 0; 13250c0d06caSMauro Carvalho Chehab } 13260c0d06caSMauro Carvalho Chehab 13270c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_forward(struct uvc_video_chain *chain, 13280c0d06caSMauro Carvalho Chehab struct uvc_entity *entity, struct uvc_entity *prev) 13290c0d06caSMauro Carvalho Chehab { 13300c0d06caSMauro Carvalho Chehab struct uvc_entity *forward; 13310c0d06caSMauro Carvalho Chehab int found; 13320c0d06caSMauro Carvalho Chehab 13330c0d06caSMauro Carvalho Chehab /* Forward scan */ 13340c0d06caSMauro Carvalho Chehab forward = NULL; 13350c0d06caSMauro Carvalho Chehab found = 0; 13360c0d06caSMauro Carvalho Chehab 13370c0d06caSMauro Carvalho Chehab while (1) { 13380c0d06caSMauro Carvalho Chehab forward = uvc_entity_by_reference(chain->dev, entity->id, 13390c0d06caSMauro Carvalho Chehab forward); 13400c0d06caSMauro Carvalho Chehab if (forward == NULL) 13410c0d06caSMauro Carvalho Chehab break; 13420c0d06caSMauro Carvalho Chehab if (forward == prev) 13430c0d06caSMauro Carvalho Chehab continue; 13440c0d06caSMauro Carvalho Chehab 13450c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(forward)) { 13460c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 13470c0d06caSMauro Carvalho Chehab if (forward->bNrInPins != 1) { 13480c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Extension unit %d " 13490c0d06caSMauro Carvalho Chehab "has more than 1 input pin.\n", 13500c0d06caSMauro Carvalho Chehab entity->id); 13510c0d06caSMauro Carvalho Chehab return -EINVAL; 13520c0d06caSMauro Carvalho Chehab } 13530c0d06caSMauro Carvalho Chehab 13540c0d06caSMauro Carvalho Chehab list_add_tail(&forward->chain, &chain->entities); 13550c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) { 13560c0d06caSMauro Carvalho Chehab if (!found) 13570c0d06caSMauro Carvalho Chehab printk(" (->"); 13580c0d06caSMauro Carvalho Chehab 13590c0d06caSMauro Carvalho Chehab printk(" XU %d", forward->id); 13600c0d06caSMauro Carvalho Chehab found = 1; 13610c0d06caSMauro Carvalho Chehab } 13620c0d06caSMauro Carvalho Chehab break; 13630c0d06caSMauro Carvalho Chehab 13640c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 13650c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 13660c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 13670c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 13680c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_IS_ITERM(forward)) { 13690c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Unsupported input " 13700c0d06caSMauro Carvalho Chehab "terminal %u.\n", forward->id); 13710c0d06caSMauro Carvalho Chehab return -EINVAL; 13720c0d06caSMauro Carvalho Chehab } 13730c0d06caSMauro Carvalho Chehab 13740c0d06caSMauro Carvalho Chehab list_add_tail(&forward->chain, &chain->entities); 13750c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) { 13760c0d06caSMauro Carvalho Chehab if (!found) 13770c0d06caSMauro Carvalho Chehab printk(" (->"); 13780c0d06caSMauro Carvalho Chehab 13790c0d06caSMauro Carvalho Chehab printk(" OT %d", forward->id); 13800c0d06caSMauro Carvalho Chehab found = 1; 13810c0d06caSMauro Carvalho Chehab } 13820c0d06caSMauro Carvalho Chehab break; 13830c0d06caSMauro Carvalho Chehab } 13840c0d06caSMauro Carvalho Chehab } 13850c0d06caSMauro Carvalho Chehab if (found) 13860c0d06caSMauro Carvalho Chehab printk(")"); 13870c0d06caSMauro Carvalho Chehab 13880c0d06caSMauro Carvalho Chehab return 0; 13890c0d06caSMauro Carvalho Chehab } 13900c0d06caSMauro Carvalho Chehab 13910c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_backward(struct uvc_video_chain *chain, 13920c0d06caSMauro Carvalho Chehab struct uvc_entity **_entity) 13930c0d06caSMauro Carvalho Chehab { 13940c0d06caSMauro Carvalho Chehab struct uvc_entity *entity = *_entity; 13950c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 13960c0d06caSMauro Carvalho Chehab int id = -EINVAL, i; 13970c0d06caSMauro Carvalho Chehab 13980c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(entity)) { 13990c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 14000c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 14010c0d06caSMauro Carvalho Chehab id = entity->baSourceID[0]; 14020c0d06caSMauro Carvalho Chehab break; 14030c0d06caSMauro Carvalho Chehab 14040c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 14050c0d06caSMauro Carvalho Chehab /* Single-input selector units are ignored. */ 14060c0d06caSMauro Carvalho Chehab if (entity->bNrInPins == 1) { 14070c0d06caSMauro Carvalho Chehab id = entity->baSourceID[0]; 14080c0d06caSMauro Carvalho Chehab break; 14090c0d06caSMauro Carvalho Chehab } 14100c0d06caSMauro Carvalho Chehab 14110c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 14120c0d06caSMauro Carvalho Chehab printk(" <- IT"); 14130c0d06caSMauro Carvalho Chehab 14140c0d06caSMauro Carvalho Chehab chain->selector = entity; 14150c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->bNrInPins; ++i) { 14160c0d06caSMauro Carvalho Chehab id = entity->baSourceID[i]; 14170c0d06caSMauro Carvalho Chehab term = uvc_entity_by_id(chain->dev, id); 14180c0d06caSMauro Carvalho Chehab if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { 14190c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Selector unit %d " 14200c0d06caSMauro Carvalho Chehab "input %d isn't connected to an " 14210c0d06caSMauro Carvalho Chehab "input terminal\n", entity->id, i); 14220c0d06caSMauro Carvalho Chehab return -1; 14230c0d06caSMauro Carvalho Chehab } 14240c0d06caSMauro Carvalho Chehab 14250c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 14260c0d06caSMauro Carvalho Chehab printk(" %d", term->id); 14270c0d06caSMauro Carvalho Chehab 14280c0d06caSMauro Carvalho Chehab list_add_tail(&term->chain, &chain->entities); 14290c0d06caSMauro Carvalho Chehab uvc_scan_chain_forward(chain, term, entity); 14300c0d06caSMauro Carvalho Chehab } 14310c0d06caSMauro Carvalho Chehab 14320c0d06caSMauro Carvalho Chehab if (uvc_trace_param & UVC_TRACE_PROBE) 14330c0d06caSMauro Carvalho Chehab printk("\n"); 14340c0d06caSMauro Carvalho Chehab 14350c0d06caSMauro Carvalho Chehab id = 0; 14360c0d06caSMauro Carvalho Chehab break; 14370c0d06caSMauro Carvalho Chehab 14380c0d06caSMauro Carvalho Chehab case UVC_ITT_VENDOR_SPECIFIC: 14390c0d06caSMauro Carvalho Chehab case UVC_ITT_CAMERA: 14400c0d06caSMauro Carvalho Chehab case UVC_ITT_MEDIA_TRANSPORT_INPUT: 14410c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 14420c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 14430c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 14440c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 14450c0d06caSMauro Carvalho Chehab id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0; 14460c0d06caSMauro Carvalho Chehab break; 14470c0d06caSMauro Carvalho Chehab } 14480c0d06caSMauro Carvalho Chehab 14490c0d06caSMauro Carvalho Chehab if (id <= 0) { 14500c0d06caSMauro Carvalho Chehab *_entity = NULL; 14510c0d06caSMauro Carvalho Chehab return id; 14520c0d06caSMauro Carvalho Chehab } 14530c0d06caSMauro Carvalho Chehab 14540c0d06caSMauro Carvalho Chehab entity = uvc_entity_by_id(chain->dev, id); 14550c0d06caSMauro Carvalho Chehab if (entity == NULL) { 14560c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found reference to " 14570c0d06caSMauro Carvalho Chehab "unknown entity %d.\n", id); 14580c0d06caSMauro Carvalho Chehab return -EINVAL; 14590c0d06caSMauro Carvalho Chehab } 14600c0d06caSMauro Carvalho Chehab 14610c0d06caSMauro Carvalho Chehab *_entity = entity; 14620c0d06caSMauro Carvalho Chehab return 0; 14630c0d06caSMauro Carvalho Chehab } 14640c0d06caSMauro Carvalho Chehab 14650c0d06caSMauro Carvalho Chehab static int uvc_scan_chain(struct uvc_video_chain *chain, 14660c0d06caSMauro Carvalho Chehab struct uvc_entity *term) 14670c0d06caSMauro Carvalho Chehab { 14680c0d06caSMauro Carvalho Chehab struct uvc_entity *entity, *prev; 14690c0d06caSMauro Carvalho Chehab 14700c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:"); 14710c0d06caSMauro Carvalho Chehab 14720c0d06caSMauro Carvalho Chehab entity = term; 14730c0d06caSMauro Carvalho Chehab prev = NULL; 14740c0d06caSMauro Carvalho Chehab 14750c0d06caSMauro Carvalho Chehab while (entity != NULL) { 14760c0d06caSMauro Carvalho Chehab /* Entity must not be part of an existing chain */ 14770c0d06caSMauro Carvalho Chehab if (entity->chain.next || entity->chain.prev) { 14780c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_DESCR, "Found reference to " 14790c0d06caSMauro Carvalho Chehab "entity %d already in chain.\n", entity->id); 14800c0d06caSMauro Carvalho Chehab return -EINVAL; 14810c0d06caSMauro Carvalho Chehab } 14820c0d06caSMauro Carvalho Chehab 14830c0d06caSMauro Carvalho Chehab /* Process entity */ 14840c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_entity(chain, entity) < 0) 14850c0d06caSMauro Carvalho Chehab return -EINVAL; 14860c0d06caSMauro Carvalho Chehab 14870c0d06caSMauro Carvalho Chehab /* Forward scan */ 14880c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_forward(chain, entity, prev) < 0) 14890c0d06caSMauro Carvalho Chehab return -EINVAL; 14900c0d06caSMauro Carvalho Chehab 14910c0d06caSMauro Carvalho Chehab /* Backward scan */ 14920c0d06caSMauro Carvalho Chehab prev = entity; 14930c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_backward(chain, &entity) < 0) 14940c0d06caSMauro Carvalho Chehab return -EINVAL; 14950c0d06caSMauro Carvalho Chehab } 14960c0d06caSMauro Carvalho Chehab 14970c0d06caSMauro Carvalho Chehab return 0; 14980c0d06caSMauro Carvalho Chehab } 14990c0d06caSMauro Carvalho Chehab 15000c0d06caSMauro Carvalho Chehab static unsigned int uvc_print_terms(struct list_head *terms, u16 dir, 15010c0d06caSMauro Carvalho Chehab char *buffer) 15020c0d06caSMauro Carvalho Chehab { 15030c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 15040c0d06caSMauro Carvalho Chehab unsigned int nterms = 0; 15050c0d06caSMauro Carvalho Chehab char *p = buffer; 15060c0d06caSMauro Carvalho Chehab 15070c0d06caSMauro Carvalho Chehab list_for_each_entry(term, terms, chain) { 15080c0d06caSMauro Carvalho Chehab if (!UVC_ENTITY_IS_TERM(term) || 15090c0d06caSMauro Carvalho Chehab UVC_TERM_DIRECTION(term) != dir) 15100c0d06caSMauro Carvalho Chehab continue; 15110c0d06caSMauro Carvalho Chehab 15120c0d06caSMauro Carvalho Chehab if (nterms) 15130c0d06caSMauro Carvalho Chehab p += sprintf(p, ","); 15140c0d06caSMauro Carvalho Chehab if (++nterms >= 4) { 15150c0d06caSMauro Carvalho Chehab p += sprintf(p, "..."); 15160c0d06caSMauro Carvalho Chehab break; 15170c0d06caSMauro Carvalho Chehab } 15180c0d06caSMauro Carvalho Chehab p += sprintf(p, "%u", term->id); 15190c0d06caSMauro Carvalho Chehab } 15200c0d06caSMauro Carvalho Chehab 15210c0d06caSMauro Carvalho Chehab return p - buffer; 15220c0d06caSMauro Carvalho Chehab } 15230c0d06caSMauro Carvalho Chehab 15240c0d06caSMauro Carvalho Chehab static const char *uvc_print_chain(struct uvc_video_chain *chain) 15250c0d06caSMauro Carvalho Chehab { 15260c0d06caSMauro Carvalho Chehab static char buffer[43]; 15270c0d06caSMauro Carvalho Chehab char *p = buffer; 15280c0d06caSMauro Carvalho Chehab 15290c0d06caSMauro Carvalho Chehab p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p); 15300c0d06caSMauro Carvalho Chehab p += sprintf(p, " -> "); 15310c0d06caSMauro Carvalho Chehab uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p); 15320c0d06caSMauro Carvalho Chehab 15330c0d06caSMauro Carvalho Chehab return buffer; 15340c0d06caSMauro Carvalho Chehab } 15350c0d06caSMauro Carvalho Chehab 15360c0d06caSMauro Carvalho Chehab /* 15370c0d06caSMauro Carvalho Chehab * Scan the device for video chains and register video devices. 15380c0d06caSMauro Carvalho Chehab * 15390c0d06caSMauro Carvalho Chehab * Chains are scanned starting at their output terminals and walked backwards. 15400c0d06caSMauro Carvalho Chehab */ 15410c0d06caSMauro Carvalho Chehab static int uvc_scan_device(struct uvc_device *dev) 15420c0d06caSMauro Carvalho Chehab { 15430c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 15440c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 15450c0d06caSMauro Carvalho Chehab 15460c0d06caSMauro Carvalho Chehab list_for_each_entry(term, &dev->entities, list) { 15470c0d06caSMauro Carvalho Chehab if (!UVC_ENTITY_IS_OTERM(term)) 15480c0d06caSMauro Carvalho Chehab continue; 15490c0d06caSMauro Carvalho Chehab 15500c0d06caSMauro Carvalho Chehab /* If the terminal is already included in a chain, skip it. 15510c0d06caSMauro Carvalho Chehab * This can happen for chains that have multiple output 15520c0d06caSMauro Carvalho Chehab * terminals, where all output terminals beside the first one 15530c0d06caSMauro Carvalho Chehab * will be inserted in the chain in forward scans. 15540c0d06caSMauro Carvalho Chehab */ 15550c0d06caSMauro Carvalho Chehab if (term->chain.next || term->chain.prev) 15560c0d06caSMauro Carvalho Chehab continue; 15570c0d06caSMauro Carvalho Chehab 15580c0d06caSMauro Carvalho Chehab chain = kzalloc(sizeof(*chain), GFP_KERNEL); 15590c0d06caSMauro Carvalho Chehab if (chain == NULL) 15600c0d06caSMauro Carvalho Chehab return -ENOMEM; 15610c0d06caSMauro Carvalho Chehab 15620c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&chain->entities); 15630c0d06caSMauro Carvalho Chehab mutex_init(&chain->ctrl_mutex); 15640c0d06caSMauro Carvalho Chehab chain->dev = dev; 15650550513cSLaurent Pinchart v4l2_prio_init(&chain->prio); 15660c0d06caSMauro Carvalho Chehab 15678be8ec6eSLaurent Pinchart term->flags |= UVC_ENTITY_FLAG_DEFAULT; 15688be8ec6eSLaurent Pinchart 15690c0d06caSMauro Carvalho Chehab if (uvc_scan_chain(chain, term) < 0) { 15700c0d06caSMauro Carvalho Chehab kfree(chain); 15710c0d06caSMauro Carvalho Chehab continue; 15720c0d06caSMauro Carvalho Chehab } 15730c0d06caSMauro Carvalho Chehab 15740c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n", 15750c0d06caSMauro Carvalho Chehab uvc_print_chain(chain)); 15760c0d06caSMauro Carvalho Chehab 15770c0d06caSMauro Carvalho Chehab list_add_tail(&chain->list, &dev->chains); 15780c0d06caSMauro Carvalho Chehab } 15790c0d06caSMauro Carvalho Chehab 15800c0d06caSMauro Carvalho Chehab if (list_empty(&dev->chains)) { 15810c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "No valid video chain found.\n"); 15820c0d06caSMauro Carvalho Chehab return -1; 15830c0d06caSMauro Carvalho Chehab } 15840c0d06caSMauro Carvalho Chehab 15850c0d06caSMauro Carvalho Chehab return 0; 15860c0d06caSMauro Carvalho Chehab } 15870c0d06caSMauro Carvalho Chehab 15880c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 15890c0d06caSMauro Carvalho Chehab * Video device registration and unregistration 15900c0d06caSMauro Carvalho Chehab */ 15910c0d06caSMauro Carvalho Chehab 15920c0d06caSMauro Carvalho Chehab /* 15930c0d06caSMauro Carvalho Chehab * Delete the UVC device. 15940c0d06caSMauro Carvalho Chehab * 15950c0d06caSMauro Carvalho Chehab * Called by the kernel when the last reference to the uvc_device structure 15960c0d06caSMauro Carvalho Chehab * is released. 15970c0d06caSMauro Carvalho Chehab * 15980c0d06caSMauro Carvalho Chehab * As this function is called after or during disconnect(), all URBs have 15990c0d06caSMauro Carvalho Chehab * already been canceled by the USB core. There is no need to kill the 16000c0d06caSMauro Carvalho Chehab * interrupt URB manually. 16010c0d06caSMauro Carvalho Chehab */ 16020c0d06caSMauro Carvalho Chehab static void uvc_delete(struct uvc_device *dev) 16030c0d06caSMauro Carvalho Chehab { 16040c0d06caSMauro Carvalho Chehab struct list_head *p, *n; 16050c0d06caSMauro Carvalho Chehab 16060c0d06caSMauro Carvalho Chehab usb_put_intf(dev->intf); 16070c0d06caSMauro Carvalho Chehab usb_put_dev(dev->udev); 16080c0d06caSMauro Carvalho Chehab 16090c0d06caSMauro Carvalho Chehab uvc_status_cleanup(dev); 16100c0d06caSMauro Carvalho Chehab uvc_ctrl_cleanup_device(dev); 16110c0d06caSMauro Carvalho Chehab 16120c0d06caSMauro Carvalho Chehab if (dev->vdev.dev) 16130c0d06caSMauro Carvalho Chehab v4l2_device_unregister(&dev->vdev); 16140c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 16150c0d06caSMauro Carvalho Chehab if (media_devnode_is_registered(&dev->mdev.devnode)) 16160c0d06caSMauro Carvalho Chehab media_device_unregister(&dev->mdev); 16170c0d06caSMauro Carvalho Chehab #endif 16180c0d06caSMauro Carvalho Chehab 16190c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->chains) { 16200c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 16210c0d06caSMauro Carvalho Chehab chain = list_entry(p, struct uvc_video_chain, list); 16220c0d06caSMauro Carvalho Chehab kfree(chain); 16230c0d06caSMauro Carvalho Chehab } 16240c0d06caSMauro Carvalho Chehab 16250c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->entities) { 16260c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 16270c0d06caSMauro Carvalho Chehab entity = list_entry(p, struct uvc_entity, list); 16280c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 16290c0d06caSMauro Carvalho Chehab uvc_mc_cleanup_entity(entity); 16300c0d06caSMauro Carvalho Chehab #endif 16310c0d06caSMauro Carvalho Chehab if (entity->vdev) { 16320c0d06caSMauro Carvalho Chehab video_device_release(entity->vdev); 16330c0d06caSMauro Carvalho Chehab entity->vdev = NULL; 16340c0d06caSMauro Carvalho Chehab } 16350c0d06caSMauro Carvalho Chehab kfree(entity); 16360c0d06caSMauro Carvalho Chehab } 16370c0d06caSMauro Carvalho Chehab 16380c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->streams) { 16390c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming; 16400c0d06caSMauro Carvalho Chehab streaming = list_entry(p, struct uvc_streaming, list); 16410c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, 16420c0d06caSMauro Carvalho Chehab streaming->intf); 16430c0d06caSMauro Carvalho Chehab usb_put_intf(streaming->intf); 16440c0d06caSMauro Carvalho Chehab kfree(streaming->format); 16450c0d06caSMauro Carvalho Chehab kfree(streaming->header.bmaControls); 16460c0d06caSMauro Carvalho Chehab kfree(streaming); 16470c0d06caSMauro Carvalho Chehab } 16480c0d06caSMauro Carvalho Chehab 16490c0d06caSMauro Carvalho Chehab kfree(dev); 16500c0d06caSMauro Carvalho Chehab } 16510c0d06caSMauro Carvalho Chehab 16520c0d06caSMauro Carvalho Chehab static void uvc_release(struct video_device *vdev) 16530c0d06caSMauro Carvalho Chehab { 16540c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream = video_get_drvdata(vdev); 16550c0d06caSMauro Carvalho Chehab struct uvc_device *dev = stream->dev; 16560c0d06caSMauro Carvalho Chehab 16570c0d06caSMauro Carvalho Chehab /* Decrement the registered streams count and delete the device when it 16580c0d06caSMauro Carvalho Chehab * reaches zero. 16590c0d06caSMauro Carvalho Chehab */ 16600c0d06caSMauro Carvalho Chehab if (atomic_dec_and_test(&dev->nstreams)) 16610c0d06caSMauro Carvalho Chehab uvc_delete(dev); 16620c0d06caSMauro Carvalho Chehab } 16630c0d06caSMauro Carvalho Chehab 16640c0d06caSMauro Carvalho Chehab /* 16650c0d06caSMauro Carvalho Chehab * Unregister the video devices. 16660c0d06caSMauro Carvalho Chehab */ 16670c0d06caSMauro Carvalho Chehab static void uvc_unregister_video(struct uvc_device *dev) 16680c0d06caSMauro Carvalho Chehab { 16690c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 16700c0d06caSMauro Carvalho Chehab 16710c0d06caSMauro Carvalho Chehab /* Unregistering all video devices might result in uvc_delete() being 16720c0d06caSMauro Carvalho Chehab * called from inside the loop if there's no open file handle. To avoid 16730c0d06caSMauro Carvalho Chehab * that, increment the stream count before iterating over the streams 16740c0d06caSMauro Carvalho Chehab * and decrement it when done. 16750c0d06caSMauro Carvalho Chehab */ 16760c0d06caSMauro Carvalho Chehab atomic_inc(&dev->nstreams); 16770c0d06caSMauro Carvalho Chehab 16780c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 16790c0d06caSMauro Carvalho Chehab if (stream->vdev == NULL) 16800c0d06caSMauro Carvalho Chehab continue; 16810c0d06caSMauro Carvalho Chehab 16820c0d06caSMauro Carvalho Chehab video_unregister_device(stream->vdev); 16830c0d06caSMauro Carvalho Chehab stream->vdev = NULL; 16840c0d06caSMauro Carvalho Chehab 16850c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup_stream(stream); 16860c0d06caSMauro Carvalho Chehab } 16870c0d06caSMauro Carvalho Chehab 16880c0d06caSMauro Carvalho Chehab /* Decrement the stream count and call uvc_delete explicitly if there 16890c0d06caSMauro Carvalho Chehab * are no stream left. 16900c0d06caSMauro Carvalho Chehab */ 16910c0d06caSMauro Carvalho Chehab if (atomic_dec_and_test(&dev->nstreams)) 16920c0d06caSMauro Carvalho Chehab uvc_delete(dev); 16930c0d06caSMauro Carvalho Chehab } 16940c0d06caSMauro Carvalho Chehab 16950c0d06caSMauro Carvalho Chehab static int uvc_register_video(struct uvc_device *dev, 16960c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream) 16970c0d06caSMauro Carvalho Chehab { 16980c0d06caSMauro Carvalho Chehab struct video_device *vdev; 16990c0d06caSMauro Carvalho Chehab int ret; 17000c0d06caSMauro Carvalho Chehab 17010c0d06caSMauro Carvalho Chehab /* Initialize the streaming interface with default streaming 17020c0d06caSMauro Carvalho Chehab * parameters. 17030c0d06caSMauro Carvalho Chehab */ 17040c0d06caSMauro Carvalho Chehab ret = uvc_video_init(stream); 17050c0d06caSMauro Carvalho Chehab if (ret < 0) { 17060c0d06caSMauro Carvalho Chehab uvc_printk(KERN_ERR, "Failed to initialize the device " 17070c0d06caSMauro Carvalho Chehab "(%d).\n", ret); 17080c0d06caSMauro Carvalho Chehab return ret; 17090c0d06caSMauro Carvalho Chehab } 17100c0d06caSMauro Carvalho Chehab 17110c0d06caSMauro Carvalho Chehab uvc_debugfs_init_stream(stream); 17120c0d06caSMauro Carvalho Chehab 17130c0d06caSMauro Carvalho Chehab /* Register the device with V4L. */ 17140c0d06caSMauro Carvalho Chehab vdev = video_device_alloc(); 17150c0d06caSMauro Carvalho Chehab if (vdev == NULL) { 17160c0d06caSMauro Carvalho Chehab uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n", 17170c0d06caSMauro Carvalho Chehab ret); 17180c0d06caSMauro Carvalho Chehab return -ENOMEM; 17190c0d06caSMauro Carvalho Chehab } 17200c0d06caSMauro Carvalho Chehab 17210c0d06caSMauro Carvalho Chehab /* We already hold a reference to dev->udev. The video device will be 17220c0d06caSMauro Carvalho Chehab * unregistered before the reference is released, so we don't need to 17230c0d06caSMauro Carvalho Chehab * get another one. 17240c0d06caSMauro Carvalho Chehab */ 17250c0d06caSMauro Carvalho Chehab vdev->v4l2_dev = &dev->vdev; 17260c0d06caSMauro Carvalho Chehab vdev->fops = &uvc_fops; 17270c0d06caSMauro Carvalho Chehab vdev->release = uvc_release; 17280550513cSLaurent Pinchart vdev->prio = &stream->chain->prio; 17290550513cSLaurent Pinchart set_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags); 1730954f340fSHans Verkuil if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 1731954f340fSHans Verkuil vdev->vfl_dir = VFL_DIR_TX; 17320c0d06caSMauro Carvalho Chehab strlcpy(vdev->name, dev->name, sizeof vdev->name); 17330c0d06caSMauro Carvalho Chehab 17340c0d06caSMauro Carvalho Chehab /* Set the driver data before calling video_register_device, otherwise 17350c0d06caSMauro Carvalho Chehab * uvc_v4l2_open might race us. 17360c0d06caSMauro Carvalho Chehab */ 17370c0d06caSMauro Carvalho Chehab stream->vdev = vdev; 17380c0d06caSMauro Carvalho Chehab video_set_drvdata(vdev, stream); 17390c0d06caSMauro Carvalho Chehab 17400c0d06caSMauro Carvalho Chehab ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); 17410c0d06caSMauro Carvalho Chehab if (ret < 0) { 17420c0d06caSMauro Carvalho Chehab uvc_printk(KERN_ERR, "Failed to register video device (%d).\n", 17430c0d06caSMauro Carvalho Chehab ret); 17440c0d06caSMauro Carvalho Chehab stream->vdev = NULL; 17450c0d06caSMauro Carvalho Chehab video_device_release(vdev); 17460c0d06caSMauro Carvalho Chehab return ret; 17470c0d06caSMauro Carvalho Chehab } 17480c0d06caSMauro Carvalho Chehab 1749f887e99aSLaurent Pinchart if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 1750f887e99aSLaurent Pinchart stream->chain->caps |= V4L2_CAP_VIDEO_CAPTURE; 1751f887e99aSLaurent Pinchart else 1752f887e99aSLaurent Pinchart stream->chain->caps |= V4L2_CAP_VIDEO_OUTPUT; 1753f887e99aSLaurent Pinchart 17540c0d06caSMauro Carvalho Chehab atomic_inc(&dev->nstreams); 17550c0d06caSMauro Carvalho Chehab return 0; 17560c0d06caSMauro Carvalho Chehab } 17570c0d06caSMauro Carvalho Chehab 17580c0d06caSMauro Carvalho Chehab /* 17590c0d06caSMauro Carvalho Chehab * Register all video devices in all chains. 17600c0d06caSMauro Carvalho Chehab */ 17610c0d06caSMauro Carvalho Chehab static int uvc_register_terms(struct uvc_device *dev, 17620c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain) 17630c0d06caSMauro Carvalho Chehab { 17640c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 17650c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 17660c0d06caSMauro Carvalho Chehab int ret; 17670c0d06caSMauro Carvalho Chehab 17680c0d06caSMauro Carvalho Chehab list_for_each_entry(term, &chain->entities, chain) { 17690c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) 17700c0d06caSMauro Carvalho Chehab continue; 17710c0d06caSMauro Carvalho Chehab 17720c0d06caSMauro Carvalho Chehab stream = uvc_stream_by_id(dev, term->id); 17730c0d06caSMauro Carvalho Chehab if (stream == NULL) { 17740c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "No streaming interface found " 17750c0d06caSMauro Carvalho Chehab "for terminal %u.", term->id); 17760c0d06caSMauro Carvalho Chehab continue; 17770c0d06caSMauro Carvalho Chehab } 17780c0d06caSMauro Carvalho Chehab 17790c0d06caSMauro Carvalho Chehab stream->chain = chain; 17800c0d06caSMauro Carvalho Chehab ret = uvc_register_video(dev, stream); 17810c0d06caSMauro Carvalho Chehab if (ret < 0) 17820c0d06caSMauro Carvalho Chehab return ret; 17830c0d06caSMauro Carvalho Chehab 17840c0d06caSMauro Carvalho Chehab term->vdev = stream->vdev; 17850c0d06caSMauro Carvalho Chehab } 17860c0d06caSMauro Carvalho Chehab 17870c0d06caSMauro Carvalho Chehab return 0; 17880c0d06caSMauro Carvalho Chehab } 17890c0d06caSMauro Carvalho Chehab 17900c0d06caSMauro Carvalho Chehab static int uvc_register_chains(struct uvc_device *dev) 17910c0d06caSMauro Carvalho Chehab { 17920c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 17930c0d06caSMauro Carvalho Chehab int ret; 17940c0d06caSMauro Carvalho Chehab 17950c0d06caSMauro Carvalho Chehab list_for_each_entry(chain, &dev->chains, list) { 17960c0d06caSMauro Carvalho Chehab ret = uvc_register_terms(dev, chain); 17970c0d06caSMauro Carvalho Chehab if (ret < 0) 17980c0d06caSMauro Carvalho Chehab return ret; 17990c0d06caSMauro Carvalho Chehab 18000c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 18010c0d06caSMauro Carvalho Chehab ret = uvc_mc_register_entities(chain); 18020c0d06caSMauro Carvalho Chehab if (ret < 0) { 18030c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "Failed to register entites " 18040c0d06caSMauro Carvalho Chehab "(%d).\n", ret); 18050c0d06caSMauro Carvalho Chehab } 18060c0d06caSMauro Carvalho Chehab #endif 18070c0d06caSMauro Carvalho Chehab } 18080c0d06caSMauro Carvalho Chehab 18090c0d06caSMauro Carvalho Chehab return 0; 18100c0d06caSMauro Carvalho Chehab } 18110c0d06caSMauro Carvalho Chehab 18120c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 18130c0d06caSMauro Carvalho Chehab * USB probe, disconnect, suspend and resume 18140c0d06caSMauro Carvalho Chehab */ 18150c0d06caSMauro Carvalho Chehab 18160c0d06caSMauro Carvalho Chehab static int uvc_probe(struct usb_interface *intf, 18170c0d06caSMauro Carvalho Chehab const struct usb_device_id *id) 18180c0d06caSMauro Carvalho Chehab { 18190c0d06caSMauro Carvalho Chehab struct usb_device *udev = interface_to_usbdev(intf); 18200c0d06caSMauro Carvalho Chehab struct uvc_device *dev; 18210c0d06caSMauro Carvalho Chehab int ret; 18220c0d06caSMauro Carvalho Chehab 18230c0d06caSMauro Carvalho Chehab if (id->idVendor && id->idProduct) 18240c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s " 18250c0d06caSMauro Carvalho Chehab "(%04x:%04x)\n", udev->devpath, id->idVendor, 18260c0d06caSMauro Carvalho Chehab id->idProduct); 18270c0d06caSMauro Carvalho Chehab else 18280c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n", 18290c0d06caSMauro Carvalho Chehab udev->devpath); 18300c0d06caSMauro Carvalho Chehab 18310c0d06caSMauro Carvalho Chehab /* Allocate memory for the device and initialize it. */ 18320c0d06caSMauro Carvalho Chehab if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL) 18330c0d06caSMauro Carvalho Chehab return -ENOMEM; 18340c0d06caSMauro Carvalho Chehab 18350c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->entities); 18360c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->chains); 18370c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->streams); 18380c0d06caSMauro Carvalho Chehab atomic_set(&dev->nstreams, 0); 18390c0d06caSMauro Carvalho Chehab atomic_set(&dev->nmappings, 0); 184017706f56SLaurent Pinchart mutex_init(&dev->lock); 18410c0d06caSMauro Carvalho Chehab 18420c0d06caSMauro Carvalho Chehab dev->udev = usb_get_dev(udev); 18430c0d06caSMauro Carvalho Chehab dev->intf = usb_get_intf(intf); 18440c0d06caSMauro Carvalho Chehab dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; 18450c0d06caSMauro Carvalho Chehab dev->quirks = (uvc_quirks_param == -1) 18460c0d06caSMauro Carvalho Chehab ? id->driver_info : uvc_quirks_param; 18470c0d06caSMauro Carvalho Chehab 18480c0d06caSMauro Carvalho Chehab if (udev->product != NULL) 18490c0d06caSMauro Carvalho Chehab strlcpy(dev->name, udev->product, sizeof dev->name); 18500c0d06caSMauro Carvalho Chehab else 18510c0d06caSMauro Carvalho Chehab snprintf(dev->name, sizeof dev->name, 18520c0d06caSMauro Carvalho Chehab "UVC Camera (%04x:%04x)", 18530c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idVendor), 18540c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idProduct)); 18550c0d06caSMauro Carvalho Chehab 18560c0d06caSMauro Carvalho Chehab /* Parse the Video Class control descriptor. */ 18570c0d06caSMauro Carvalho Chehab if (uvc_parse_control(dev) < 0) { 18580c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC " 18590c0d06caSMauro Carvalho Chehab "descriptors.\n"); 18600c0d06caSMauro Carvalho Chehab goto error; 18610c0d06caSMauro Carvalho Chehab } 18620c0d06caSMauro Carvalho Chehab 18630c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n", 18640c0d06caSMauro Carvalho Chehab dev->uvc_version >> 8, dev->uvc_version & 0xff, 18650c0d06caSMauro Carvalho Chehab udev->product ? udev->product : "<unnamed>", 18660c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idVendor), 18670c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idProduct)); 18680c0d06caSMauro Carvalho Chehab 18690c0d06caSMauro Carvalho Chehab if (dev->quirks != id->driver_info) { 18700c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module " 18710c0d06caSMauro Carvalho Chehab "parameter for testing purpose.\n", dev->quirks); 18720c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "Please report required quirks to the " 18730c0d06caSMauro Carvalho Chehab "linux-uvc-devel mailing list.\n"); 18740c0d06caSMauro Carvalho Chehab } 18750c0d06caSMauro Carvalho Chehab 18760c0d06caSMauro Carvalho Chehab /* Register the media and V4L2 devices. */ 18770c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 18780c0d06caSMauro Carvalho Chehab dev->mdev.dev = &intf->dev; 18790c0d06caSMauro Carvalho Chehab strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model)); 18800c0d06caSMauro Carvalho Chehab if (udev->serial) 18810c0d06caSMauro Carvalho Chehab strlcpy(dev->mdev.serial, udev->serial, 18820c0d06caSMauro Carvalho Chehab sizeof(dev->mdev.serial)); 18830c0d06caSMauro Carvalho Chehab strcpy(dev->mdev.bus_info, udev->devpath); 18840c0d06caSMauro Carvalho Chehab dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); 18850c0d06caSMauro Carvalho Chehab dev->mdev.driver_version = LINUX_VERSION_CODE; 18860c0d06caSMauro Carvalho Chehab if (media_device_register(&dev->mdev) < 0) 18870c0d06caSMauro Carvalho Chehab goto error; 18880c0d06caSMauro Carvalho Chehab 18890c0d06caSMauro Carvalho Chehab dev->vdev.mdev = &dev->mdev; 18900c0d06caSMauro Carvalho Chehab #endif 18910c0d06caSMauro Carvalho Chehab if (v4l2_device_register(&intf->dev, &dev->vdev) < 0) 18920c0d06caSMauro Carvalho Chehab goto error; 18930c0d06caSMauro Carvalho Chehab 18940c0d06caSMauro Carvalho Chehab /* Initialize controls. */ 18950c0d06caSMauro Carvalho Chehab if (uvc_ctrl_init_device(dev) < 0) 18960c0d06caSMauro Carvalho Chehab goto error; 18970c0d06caSMauro Carvalho Chehab 18980c0d06caSMauro Carvalho Chehab /* Scan the device for video chains. */ 18990c0d06caSMauro Carvalho Chehab if (uvc_scan_device(dev) < 0) 19000c0d06caSMauro Carvalho Chehab goto error; 19010c0d06caSMauro Carvalho Chehab 19020c0d06caSMauro Carvalho Chehab /* Register video device nodes. */ 19030c0d06caSMauro Carvalho Chehab if (uvc_register_chains(dev) < 0) 19040c0d06caSMauro Carvalho Chehab goto error; 19050c0d06caSMauro Carvalho Chehab 19060c0d06caSMauro Carvalho Chehab /* Save our data pointer in the interface data. */ 19070c0d06caSMauro Carvalho Chehab usb_set_intfdata(intf, dev); 19080c0d06caSMauro Carvalho Chehab 19090c0d06caSMauro Carvalho Chehab /* Initialize the interrupt URB. */ 19100c0d06caSMauro Carvalho Chehab if ((ret = uvc_status_init(dev)) < 0) { 19110c0d06caSMauro Carvalho Chehab uvc_printk(KERN_INFO, "Unable to initialize the status " 19120c0d06caSMauro Carvalho Chehab "endpoint (%d), status interrupt will not be " 19130c0d06caSMauro Carvalho Chehab "supported.\n", ret); 19140c0d06caSMauro Carvalho Chehab } 19150c0d06caSMauro Carvalho Chehab 19160c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n"); 19170c0d06caSMauro Carvalho Chehab usb_enable_autosuspend(udev); 19180c0d06caSMauro Carvalho Chehab return 0; 19190c0d06caSMauro Carvalho Chehab 19200c0d06caSMauro Carvalho Chehab error: 19210c0d06caSMauro Carvalho Chehab uvc_unregister_video(dev); 19220c0d06caSMauro Carvalho Chehab return -ENODEV; 19230c0d06caSMauro Carvalho Chehab } 19240c0d06caSMauro Carvalho Chehab 19250c0d06caSMauro Carvalho Chehab static void uvc_disconnect(struct usb_interface *intf) 19260c0d06caSMauro Carvalho Chehab { 19270c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 19280c0d06caSMauro Carvalho Chehab 19290c0d06caSMauro Carvalho Chehab /* Set the USB interface data to NULL. This can be done outside the 19300c0d06caSMauro Carvalho Chehab * lock, as there's no other reader. 19310c0d06caSMauro Carvalho Chehab */ 19320c0d06caSMauro Carvalho Chehab usb_set_intfdata(intf, NULL); 19330c0d06caSMauro Carvalho Chehab 19340c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 19350c0d06caSMauro Carvalho Chehab UVC_SC_VIDEOSTREAMING) 19360c0d06caSMauro Carvalho Chehab return; 19370c0d06caSMauro Carvalho Chehab 19380c0d06caSMauro Carvalho Chehab dev->state |= UVC_DEV_DISCONNECTED; 19390c0d06caSMauro Carvalho Chehab 19400c0d06caSMauro Carvalho Chehab uvc_unregister_video(dev); 19410c0d06caSMauro Carvalho Chehab } 19420c0d06caSMauro Carvalho Chehab 19430c0d06caSMauro Carvalho Chehab static int uvc_suspend(struct usb_interface *intf, pm_message_t message) 19440c0d06caSMauro Carvalho Chehab { 19450c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 19460c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 19470c0d06caSMauro Carvalho Chehab 19480c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n", 19490c0d06caSMauro Carvalho Chehab intf->cur_altsetting->desc.bInterfaceNumber); 19500c0d06caSMauro Carvalho Chehab 19510c0d06caSMauro Carvalho Chehab /* Controls are cached on the fly so they don't need to be saved. */ 19520c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 195317706f56SLaurent Pinchart UVC_SC_VIDEOCONTROL) { 195417706f56SLaurent Pinchart mutex_lock(&dev->lock); 195517706f56SLaurent Pinchart if (dev->users) 195617706f56SLaurent Pinchart uvc_status_stop(dev); 195717706f56SLaurent Pinchart mutex_unlock(&dev->lock); 195817706f56SLaurent Pinchart return 0; 195917706f56SLaurent Pinchart } 19600c0d06caSMauro Carvalho Chehab 19610c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 19620c0d06caSMauro Carvalho Chehab if (stream->intf == intf) 19630c0d06caSMauro Carvalho Chehab return uvc_video_suspend(stream); 19640c0d06caSMauro Carvalho Chehab } 19650c0d06caSMauro Carvalho Chehab 19660c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface " 19670c0d06caSMauro Carvalho Chehab "mismatch.\n"); 19680c0d06caSMauro Carvalho Chehab return -EINVAL; 19690c0d06caSMauro Carvalho Chehab } 19700c0d06caSMauro Carvalho Chehab 19710c0d06caSMauro Carvalho Chehab static int __uvc_resume(struct usb_interface *intf, int reset) 19720c0d06caSMauro Carvalho Chehab { 19730c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 19740c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 19750c0d06caSMauro Carvalho Chehab 19760c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n", 19770c0d06caSMauro Carvalho Chehab intf->cur_altsetting->desc.bInterfaceNumber); 19780c0d06caSMauro Carvalho Chehab 19790c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 19800c0d06caSMauro Carvalho Chehab UVC_SC_VIDEOCONTROL) { 198117706f56SLaurent Pinchart int ret = 0; 19820c0d06caSMauro Carvalho Chehab 198317706f56SLaurent Pinchart if (reset) { 198417706f56SLaurent Pinchart ret = uvc_ctrl_resume_device(dev); 19850c0d06caSMauro Carvalho Chehab if (ret < 0) 19860c0d06caSMauro Carvalho Chehab return ret; 19870c0d06caSMauro Carvalho Chehab } 19880c0d06caSMauro Carvalho Chehab 198917706f56SLaurent Pinchart mutex_lock(&dev->lock); 199017706f56SLaurent Pinchart if (dev->users) 199117706f56SLaurent Pinchart ret = uvc_status_start(dev, GFP_NOIO); 199217706f56SLaurent Pinchart mutex_unlock(&dev->lock); 199317706f56SLaurent Pinchart 199417706f56SLaurent Pinchart return ret; 19950c0d06caSMauro Carvalho Chehab } 19960c0d06caSMauro Carvalho Chehab 19970c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 19980c0d06caSMauro Carvalho Chehab if (stream->intf == intf) 19990c0d06caSMauro Carvalho Chehab return uvc_video_resume(stream, reset); 20000c0d06caSMauro Carvalho Chehab } 20010c0d06caSMauro Carvalho Chehab 20020c0d06caSMauro Carvalho Chehab uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface " 20030c0d06caSMauro Carvalho Chehab "mismatch.\n"); 20040c0d06caSMauro Carvalho Chehab return -EINVAL; 20050c0d06caSMauro Carvalho Chehab } 20060c0d06caSMauro Carvalho Chehab 20070c0d06caSMauro Carvalho Chehab static int uvc_resume(struct usb_interface *intf) 20080c0d06caSMauro Carvalho Chehab { 20090c0d06caSMauro Carvalho Chehab return __uvc_resume(intf, 0); 20100c0d06caSMauro Carvalho Chehab } 20110c0d06caSMauro Carvalho Chehab 20120c0d06caSMauro Carvalho Chehab static int uvc_reset_resume(struct usb_interface *intf) 20130c0d06caSMauro Carvalho Chehab { 20140c0d06caSMauro Carvalho Chehab return __uvc_resume(intf, 1); 20150c0d06caSMauro Carvalho Chehab } 20160c0d06caSMauro Carvalho Chehab 20170c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 20180c0d06caSMauro Carvalho Chehab * Module parameters 20190c0d06caSMauro Carvalho Chehab */ 20200c0d06caSMauro Carvalho Chehab 20210c0d06caSMauro Carvalho Chehab static int uvc_clock_param_get(char *buffer, struct kernel_param *kp) 20220c0d06caSMauro Carvalho Chehab { 20230c0d06caSMauro Carvalho Chehab if (uvc_clock_param == CLOCK_MONOTONIC) 20240c0d06caSMauro Carvalho Chehab return sprintf(buffer, "CLOCK_MONOTONIC"); 20250c0d06caSMauro Carvalho Chehab else 20260c0d06caSMauro Carvalho Chehab return sprintf(buffer, "CLOCK_REALTIME"); 20270c0d06caSMauro Carvalho Chehab } 20280c0d06caSMauro Carvalho Chehab 20290c0d06caSMauro Carvalho Chehab static int uvc_clock_param_set(const char *val, struct kernel_param *kp) 20300c0d06caSMauro Carvalho Chehab { 20310c0d06caSMauro Carvalho Chehab if (strncasecmp(val, "clock_", strlen("clock_")) == 0) 20320c0d06caSMauro Carvalho Chehab val += strlen("clock_"); 20330c0d06caSMauro Carvalho Chehab 20340c0d06caSMauro Carvalho Chehab if (strcasecmp(val, "monotonic") == 0) 20350c0d06caSMauro Carvalho Chehab uvc_clock_param = CLOCK_MONOTONIC; 20360c0d06caSMauro Carvalho Chehab else if (strcasecmp(val, "realtime") == 0) 20370c0d06caSMauro Carvalho Chehab uvc_clock_param = CLOCK_REALTIME; 20380c0d06caSMauro Carvalho Chehab else 20390c0d06caSMauro Carvalho Chehab return -EINVAL; 20400c0d06caSMauro Carvalho Chehab 20410c0d06caSMauro Carvalho Chehab return 0; 20420c0d06caSMauro Carvalho Chehab } 20430c0d06caSMauro Carvalho Chehab 20440c0d06caSMauro Carvalho Chehab module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get, 20450c0d06caSMauro Carvalho Chehab &uvc_clock_param, S_IRUGO|S_IWUSR); 20460c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(clock, "Video buffers timestamp clock"); 20470c0d06caSMauro Carvalho Chehab module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR); 20480c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); 20490c0d06caSMauro Carvalho Chehab module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); 20500c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(quirks, "Forced device quirks"); 20510c0d06caSMauro Carvalho Chehab module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR); 20520c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(trace, "Trace level bitmask"); 20530c0d06caSMauro Carvalho Chehab module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR); 20540c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); 20550c0d06caSMauro Carvalho Chehab 20560c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 20570c0d06caSMauro Carvalho Chehab * Driver initialization and cleanup 20580c0d06caSMauro Carvalho Chehab */ 20590c0d06caSMauro Carvalho Chehab 20600c0d06caSMauro Carvalho Chehab /* 20610c0d06caSMauro Carvalho Chehab * The Logitech cameras listed below have their interface class set to 20620c0d06caSMauro Carvalho Chehab * VENDOR_SPEC because they don't announce themselves as UVC devices, even 20630c0d06caSMauro Carvalho Chehab * though they are compliant. 20640c0d06caSMauro Carvalho Chehab */ 20650c0d06caSMauro Carvalho Chehab static struct usb_device_id uvc_ids[] = { 20660c0d06caSMauro Carvalho Chehab /* LogiLink Wireless Webcam */ 20670c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 20680c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 20690c0d06caSMauro Carvalho Chehab .idVendor = 0x0416, 20700c0d06caSMauro Carvalho Chehab .idProduct = 0xa91a, 20710c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 20720c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 20730c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 20740c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 20750c0d06caSMauro Carvalho Chehab /* Genius eFace 2025 */ 20760c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 20770c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 20780c0d06caSMauro Carvalho Chehab .idVendor = 0x0458, 20790c0d06caSMauro Carvalho Chehab .idProduct = 0x706e, 20800c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 20810c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 20820c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 20830c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 20840c0d06caSMauro Carvalho Chehab /* Microsoft Lifecam NX-6000 */ 20850c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 20860c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 20870c0d06caSMauro Carvalho Chehab .idVendor = 0x045e, 20880c0d06caSMauro Carvalho Chehab .idProduct = 0x00f8, 20890c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 20900c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 20910c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 20920c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 2093*1558ec83SLaurent Pinchart /* Microsoft Lifecam NX-3000 */ 2094*1558ec83SLaurent Pinchart { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2095*1558ec83SLaurent Pinchart | USB_DEVICE_ID_MATCH_INT_INFO, 2096*1558ec83SLaurent Pinchart .idVendor = 0x045e, 2097*1558ec83SLaurent Pinchart .idProduct = 0x0721, 2098*1558ec83SLaurent Pinchart .bInterfaceClass = USB_CLASS_VIDEO, 2099*1558ec83SLaurent Pinchart .bInterfaceSubClass = 1, 2100*1558ec83SLaurent Pinchart .bInterfaceProtocol = 0, 2101*1558ec83SLaurent Pinchart .driver_info = UVC_QUIRK_PROBE_DEF }, 21020c0d06caSMauro Carvalho Chehab /* Microsoft Lifecam VX-7000 */ 21030c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 21040c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 21050c0d06caSMauro Carvalho Chehab .idVendor = 0x045e, 21060c0d06caSMauro Carvalho Chehab .idProduct = 0x0723, 21070c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 21080c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 21090c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 21100c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 21110c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Fusion */ 21120c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 21130c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 21140c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 21150c0d06caSMauro Carvalho Chehab .idProduct = 0x08c1, 21160c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 21170c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 21180c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 21190c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Orbit MP */ 21200c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 21210c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 21220c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 21230c0d06caSMauro Carvalho Chehab .idProduct = 0x08c2, 21240c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 21250c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 21260c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 21270c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Pro for Notebook */ 21280c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 21290c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 21300c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 21310c0d06caSMauro Carvalho Chehab .idProduct = 0x08c3, 21320c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 21330c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 21340c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 21350c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Pro 5000 */ 21360c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 21370c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 21380c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 21390c0d06caSMauro Carvalho Chehab .idProduct = 0x08c5, 21400c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 21410c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 21420c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 21430c0d06caSMauro Carvalho Chehab /* Logitech Quickcam OEM Dell Notebook */ 21440c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 21450c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 21460c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 21470c0d06caSMauro Carvalho Chehab .idProduct = 0x08c6, 21480c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 21490c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 21500c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 21510c0d06caSMauro Carvalho Chehab /* Logitech Quickcam OEM Cisco VT Camera II */ 21520c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 21530c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 21540c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 21550c0d06caSMauro Carvalho Chehab .idProduct = 0x08c7, 21560c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 21570c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 21580c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 21590c0d06caSMauro Carvalho Chehab /* Chicony CNF7129 (Asus EEE 100HE) */ 21600c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 21610c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 21620c0d06caSMauro Carvalho Chehab .idVendor = 0x04f2, 21630c0d06caSMauro Carvalho Chehab .idProduct = 0xb071, 21640c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 21650c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 21660c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 21670c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_RESTRICT_FRAME_RATE }, 21680c0d06caSMauro Carvalho Chehab /* Alcor Micro AU3820 (Future Boy PC USB Webcam) */ 21690c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 21700c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 21710c0d06caSMauro Carvalho Chehab .idVendor = 0x058f, 21720c0d06caSMauro Carvalho Chehab .idProduct = 0x3820, 21730c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 21740c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 21750c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 21760c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 21770c0d06caSMauro Carvalho Chehab /* Dell XPS m1530 */ 21780c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 21790c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 21800c0d06caSMauro Carvalho Chehab .idVendor = 0x05a9, 21810c0d06caSMauro Carvalho Chehab .idProduct = 0x2640, 21820c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 21830c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 21840c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 21850c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_DEF }, 218689e0f248SJoseph Salisbury /* Dell SP2008WFP Monitor */ 218789e0f248SJoseph Salisbury { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 218889e0f248SJoseph Salisbury | USB_DEVICE_ID_MATCH_INT_INFO, 218989e0f248SJoseph Salisbury .idVendor = 0x05a9, 219089e0f248SJoseph Salisbury .idProduct = 0x2641, 219189e0f248SJoseph Salisbury .bInterfaceClass = USB_CLASS_VIDEO, 219289e0f248SJoseph Salisbury .bInterfaceSubClass = 1, 219389e0f248SJoseph Salisbury .bInterfaceProtocol = 0, 219489e0f248SJoseph Salisbury .driver_info = UVC_QUIRK_PROBE_DEF }, 2195c2a273b2SJoseph Salisbury /* Dell Alienware X51 */ 2196c2a273b2SJoseph Salisbury { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2197c2a273b2SJoseph Salisbury | USB_DEVICE_ID_MATCH_INT_INFO, 2198c2a273b2SJoseph Salisbury .idVendor = 0x05a9, 2199c2a273b2SJoseph Salisbury .idProduct = 0x2643, 2200c2a273b2SJoseph Salisbury .bInterfaceClass = USB_CLASS_VIDEO, 2201c2a273b2SJoseph Salisbury .bInterfaceSubClass = 1, 2202c2a273b2SJoseph Salisbury .bInterfaceProtocol = 0, 2203c2a273b2SJoseph Salisbury .driver_info = UVC_QUIRK_PROBE_DEF }, 2204afcf44c7SKamal Mostafa /* Dell Studio Hybrid 140g (OmniVision webcam) */ 2205afcf44c7SKamal Mostafa { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2206afcf44c7SKamal Mostafa | USB_DEVICE_ID_MATCH_INT_INFO, 2207afcf44c7SKamal Mostafa .idVendor = 0x05a9, 2208afcf44c7SKamal Mostafa .idProduct = 0x264a, 2209afcf44c7SKamal Mostafa .bInterfaceClass = USB_CLASS_VIDEO, 2210afcf44c7SKamal Mostafa .bInterfaceSubClass = 1, 2211afcf44c7SKamal Mostafa .bInterfaceProtocol = 0, 2212afcf44c7SKamal Mostafa .driver_info = UVC_QUIRK_PROBE_DEF }, 22130c0d06caSMauro Carvalho Chehab /* Apple Built-In iSight */ 22140c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 22150c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 22160c0d06caSMauro Carvalho Chehab .idVendor = 0x05ac, 22170c0d06caSMauro Carvalho Chehab .idProduct = 0x8501, 22180c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 22190c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 22200c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 22210c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX 22220c0d06caSMauro Carvalho Chehab | UVC_QUIRK_BUILTIN_ISIGHT }, 22230c0d06caSMauro Carvalho Chehab /* Foxlink ("HP Webcam" on HP Mini 5103) */ 22240c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 22250c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 22260c0d06caSMauro Carvalho Chehab .idVendor = 0x05c8, 22270c0d06caSMauro Carvalho Chehab .idProduct = 0x0403, 22280c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 22290c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 22300c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 22310c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, 22320c0d06caSMauro Carvalho Chehab /* Genesys Logic USB 2.0 PC Camera */ 22330c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 22340c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 22350c0d06caSMauro Carvalho Chehab .idVendor = 0x05e3, 22360c0d06caSMauro Carvalho Chehab .idProduct = 0x0505, 22370c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 22380c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 22390c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 22400c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 22410c0d06caSMauro Carvalho Chehab /* Hercules Classic Silver */ 22420c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 22430c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 22440c0d06caSMauro Carvalho Chehab .idVendor = 0x06f8, 22450c0d06caSMauro Carvalho Chehab .idProduct = 0x300c, 22460c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 22470c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 22480c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 22490c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, 22500c0d06caSMauro Carvalho Chehab /* ViMicro Vega */ 22510c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 22520c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 22530c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 22540c0d06caSMauro Carvalho Chehab .idProduct = 0x332d, 22550c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 22560c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 22570c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 22580c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, 22590c0d06caSMauro Carvalho Chehab /* ViMicro - Minoru3D */ 22600c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 22610c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 22620c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 22630c0d06caSMauro Carvalho Chehab .idProduct = 0x3410, 22640c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 22650c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 22660c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 22670c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, 22680c0d06caSMauro Carvalho Chehab /* ViMicro Venus - Minoru3D */ 22690c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 22700c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 22710c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 22720c0d06caSMauro Carvalho Chehab .idProduct = 0x3420, 22730c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 22740c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 22750c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 22760c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, 22770c0d06caSMauro Carvalho Chehab /* Ophir Optronics - SPCAM 620U */ 22780c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 22790c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 22800c0d06caSMauro Carvalho Chehab .idVendor = 0x0bd3, 22810c0d06caSMauro Carvalho Chehab .idProduct = 0x0555, 22820c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 22830c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 22840c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 22850c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 22860c0d06caSMauro Carvalho Chehab /* MT6227 */ 22870c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 22880c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 22890c0d06caSMauro Carvalho Chehab .idVendor = 0x0e8d, 22900c0d06caSMauro Carvalho Chehab .idProduct = 0x0004, 22910c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 22920c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 22930c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 22940c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX 22950c0d06caSMauro Carvalho Chehab | UVC_QUIRK_PROBE_DEF }, 22960c0d06caSMauro Carvalho Chehab /* IMC Networks (Medion Akoya) */ 22970c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 22980c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 22990c0d06caSMauro Carvalho Chehab .idVendor = 0x13d3, 23000c0d06caSMauro Carvalho Chehab .idProduct = 0x5103, 23010c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 23020c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 23030c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 23040c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 23050c0d06caSMauro Carvalho Chehab /* JMicron USB2.0 XGA WebCam */ 23060c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 23070c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 23080c0d06caSMauro Carvalho Chehab .idVendor = 0x152d, 23090c0d06caSMauro Carvalho Chehab .idProduct = 0x0310, 23100c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 23110c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 23120c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 23130c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 23140c0d06caSMauro Carvalho Chehab /* Syntek (HP Spartan) */ 23150c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 23160c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 23170c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 23180c0d06caSMauro Carvalho Chehab .idProduct = 0x5212, 23190c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 23200c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 23210c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 23220c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 23230c0d06caSMauro Carvalho Chehab /* Syntek (Samsung Q310) */ 23240c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 23250c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 23260c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 23270c0d06caSMauro Carvalho Chehab .idProduct = 0x5931, 23280c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 23290c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 23300c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 23310c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 23320c0d06caSMauro Carvalho Chehab /* Syntek (Packard Bell EasyNote MX52 */ 23330c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 23340c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 23350c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 23360c0d06caSMauro Carvalho Chehab .idProduct = 0x8a12, 23370c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 23380c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 23390c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 23400c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 23410c0d06caSMauro Carvalho Chehab /* Syntek (Asus F9SG) */ 23420c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 23430c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 23440c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 23450c0d06caSMauro Carvalho Chehab .idProduct = 0x8a31, 23460c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 23470c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 23480c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 23490c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 23500c0d06caSMauro Carvalho Chehab /* Syntek (Asus U3S) */ 23510c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 23520c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 23530c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 23540c0d06caSMauro Carvalho Chehab .idProduct = 0x8a33, 23550c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 23560c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 23570c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 23580c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 23590c0d06caSMauro Carvalho Chehab /* Syntek (JAOtech Smart Terminal) */ 23600c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 23610c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 23620c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 23630c0d06caSMauro Carvalho Chehab .idProduct = 0x8a34, 23640c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 23650c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 23660c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 23670c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 23680c0d06caSMauro Carvalho Chehab /* Miricle 307K */ 23690c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 23700c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 23710c0d06caSMauro Carvalho Chehab .idVendor = 0x17dc, 23720c0d06caSMauro Carvalho Chehab .idProduct = 0x0202, 23730c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 23740c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 23750c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 23760c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 23770c0d06caSMauro Carvalho Chehab /* Lenovo Thinkpad SL400/SL500 */ 23780c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 23790c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 23800c0d06caSMauro Carvalho Chehab .idVendor = 0x17ef, 23810c0d06caSMauro Carvalho Chehab .idProduct = 0x480b, 23820c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 23830c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 23840c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 23850c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STREAM_NO_FID }, 23860c0d06caSMauro Carvalho Chehab /* Aveo Technology USB 2.0 Camera */ 23870c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 23880c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 23890c0d06caSMauro Carvalho Chehab .idVendor = 0x1871, 23900c0d06caSMauro Carvalho Chehab .idProduct = 0x0306, 23910c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 23920c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 23930c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 23940c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX 23950c0d06caSMauro Carvalho Chehab | UVC_QUIRK_PROBE_EXTRAFIELDS }, 23960c0d06caSMauro Carvalho Chehab /* Ecamm Pico iMage */ 23970c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 23980c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 23990c0d06caSMauro Carvalho Chehab .idVendor = 0x18cd, 24000c0d06caSMauro Carvalho Chehab .idProduct = 0xcafe, 24010c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 24020c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24030c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 24040c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS }, 24050c0d06caSMauro Carvalho Chehab /* Manta MM-353 Plako */ 24060c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24070c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24080c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 24090c0d06caSMauro Carvalho Chehab .idProduct = 0x3188, 24100c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 24110c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24120c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 24130c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 24140c0d06caSMauro Carvalho Chehab /* FSC WebCam V30S */ 24150c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24160c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24170c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 24180c0d06caSMauro Carvalho Chehab .idProduct = 0x3288, 24190c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 24200c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24210c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 24220c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 24230c0d06caSMauro Carvalho Chehab /* Arkmicro unbranded */ 24240c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24250c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24260c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 24270c0d06caSMauro Carvalho Chehab .idProduct = 0x3290, 24280c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 24290c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24300c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 24310c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_DEF }, 24320c0d06caSMauro Carvalho Chehab /* The Imaging Source USB CCD cameras */ 24330c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24340c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24350c0d06caSMauro Carvalho Chehab .idVendor = 0x199e, 24360c0d06caSMauro Carvalho Chehab .idProduct = 0x8102, 24370c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 24380c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24390c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 24400c0d06caSMauro Carvalho Chehab /* Bodelin ProScopeHR */ 24410c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24420c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_DEV_HI 24430c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24440c0d06caSMauro Carvalho Chehab .idVendor = 0x19ab, 24450c0d06caSMauro Carvalho Chehab .idProduct = 0x1000, 24460c0d06caSMauro Carvalho Chehab .bcdDevice_hi = 0x0126, 24470c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 24480c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24490c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 24500c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_STATUS_INTERVAL }, 24510c0d06caSMauro Carvalho Chehab /* MSI StarCam 370i */ 24520c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24530c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24540c0d06caSMauro Carvalho Chehab .idVendor = 0x1b3b, 24550c0d06caSMauro Carvalho Chehab .idProduct = 0x2951, 24560c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 24570c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24580c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 24590c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX }, 24600c0d06caSMauro Carvalho Chehab /* SiGma Micro USB Web Camera */ 24610c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24620c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24630c0d06caSMauro Carvalho Chehab .idVendor = 0x1c4f, 24640c0d06caSMauro Carvalho Chehab .idProduct = 0x3000, 24650c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 24660c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24670c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 24680c0d06caSMauro Carvalho Chehab .driver_info = UVC_QUIRK_PROBE_MINMAX 24690c0d06caSMauro Carvalho Chehab | UVC_QUIRK_IGNORE_SELECTOR_UNIT }, 24700c0d06caSMauro Carvalho Chehab /* Generic USB Video Class */ 24710c0d06caSMauro Carvalho Chehab { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, 24720c0d06caSMauro Carvalho Chehab {} 24730c0d06caSMauro Carvalho Chehab }; 24740c0d06caSMauro Carvalho Chehab 24750c0d06caSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, uvc_ids); 24760c0d06caSMauro Carvalho Chehab 24770c0d06caSMauro Carvalho Chehab struct uvc_driver uvc_driver = { 24780c0d06caSMauro Carvalho Chehab .driver = { 24790c0d06caSMauro Carvalho Chehab .name = "uvcvideo", 24800c0d06caSMauro Carvalho Chehab .probe = uvc_probe, 24810c0d06caSMauro Carvalho Chehab .disconnect = uvc_disconnect, 24820c0d06caSMauro Carvalho Chehab .suspend = uvc_suspend, 24830c0d06caSMauro Carvalho Chehab .resume = uvc_resume, 24840c0d06caSMauro Carvalho Chehab .reset_resume = uvc_reset_resume, 24850c0d06caSMauro Carvalho Chehab .id_table = uvc_ids, 24860c0d06caSMauro Carvalho Chehab .supports_autosuspend = 1, 24870c0d06caSMauro Carvalho Chehab }, 24880c0d06caSMauro Carvalho Chehab }; 24890c0d06caSMauro Carvalho Chehab 24900c0d06caSMauro Carvalho Chehab static int __init uvc_init(void) 24910c0d06caSMauro Carvalho Chehab { 24920c0d06caSMauro Carvalho Chehab int ret; 24930c0d06caSMauro Carvalho Chehab 24940c0d06caSMauro Carvalho Chehab uvc_debugfs_init(); 24950c0d06caSMauro Carvalho Chehab 24960c0d06caSMauro Carvalho Chehab ret = usb_register(&uvc_driver.driver); 24970c0d06caSMauro Carvalho Chehab if (ret < 0) { 24980c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup(); 24990c0d06caSMauro Carvalho Chehab return ret; 25000c0d06caSMauro Carvalho Chehab } 25010c0d06caSMauro Carvalho Chehab 25020c0d06caSMauro Carvalho Chehab printk(KERN_INFO DRIVER_DESC " (" DRIVER_VERSION ")\n"); 25030c0d06caSMauro Carvalho Chehab return 0; 25040c0d06caSMauro Carvalho Chehab } 25050c0d06caSMauro Carvalho Chehab 25060c0d06caSMauro Carvalho Chehab static void __exit uvc_cleanup(void) 25070c0d06caSMauro Carvalho Chehab { 25080c0d06caSMauro Carvalho Chehab usb_deregister(&uvc_driver.driver); 25090c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup(); 25100c0d06caSMauro Carvalho Chehab } 25110c0d06caSMauro Carvalho Chehab 25120c0d06caSMauro Carvalho Chehab module_init(uvc_init); 25130c0d06caSMauro Carvalho Chehab module_exit(uvc_cleanup); 25140c0d06caSMauro Carvalho Chehab 25150c0d06caSMauro Carvalho Chehab MODULE_AUTHOR(DRIVER_AUTHOR); 25160c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION(DRIVER_DESC); 25170c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 25180c0d06caSMauro Carvalho Chehab MODULE_VERSION(DRIVER_VERSION); 25190c0d06caSMauro Carvalho Chehab 2520