12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 20c0d06caSMauro Carvalho Chehab /* 30c0d06caSMauro Carvalho Chehab * uvc_driver.c -- USB Video Class driver 40c0d06caSMauro Carvalho Chehab * 50c0d06caSMauro Carvalho Chehab * Copyright (C) 2005-2010 60c0d06caSMauro Carvalho Chehab * Laurent Pinchart (laurent.pinchart@ideasonboard.com) 70c0d06caSMauro Carvalho Chehab */ 80c0d06caSMauro Carvalho Chehab 90c0d06caSMauro Carvalho Chehab #include <linux/atomic.h> 102886477fSRicardo Ribalda #include <linux/gpio/consumer.h> 110c0d06caSMauro Carvalho Chehab #include <linux/kernel.h> 120c0d06caSMauro Carvalho Chehab #include <linux/list.h> 130c0d06caSMauro Carvalho Chehab #include <linux/module.h> 140c0d06caSMauro Carvalho Chehab #include <linux/slab.h> 150c0d06caSMauro Carvalho Chehab #include <linux/usb.h> 160c0d06caSMauro Carvalho Chehab #include <linux/videodev2.h> 170c0d06caSMauro Carvalho Chehab #include <linux/vmalloc.h> 180c0d06caSMauro Carvalho Chehab #include <linux/wait.h> 190c0d06caSMauro Carvalho Chehab #include <asm/unaligned.h> 200c0d06caSMauro Carvalho Chehab 210c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h> 2231a96f4cSLaurent Pinchart #include <media/v4l2-ioctl.h> 236b028df7SMichael Grzeschik #include <media/v4l2-uvc.h> 240c0d06caSMauro Carvalho Chehab 250c0d06caSMauro Carvalho Chehab #include "uvcvideo.h" 260c0d06caSMauro Carvalho Chehab 270c0d06caSMauro Carvalho Chehab #define DRIVER_AUTHOR "Laurent Pinchart " \ 280c0d06caSMauro Carvalho Chehab "<laurent.pinchart@ideasonboard.com>" 290c0d06caSMauro Carvalho Chehab #define DRIVER_DESC "USB Video Class driver" 300c0d06caSMauro Carvalho Chehab 310c0d06caSMauro Carvalho Chehab unsigned int uvc_clock_param = CLOCK_MONOTONIC; 325d0fd3c8SLaurent Pinchart unsigned int uvc_hw_timestamps_param; 330c0d06caSMauro Carvalho Chehab unsigned int uvc_no_drop_param; 340c0d06caSMauro Carvalho Chehab static unsigned int uvc_quirks_param = -1; 359e56380aSJoe Perches unsigned int uvc_dbg_param; 360c0d06caSMauro Carvalho Chehab unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; 370c0d06caSMauro Carvalho Chehab 380c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 390c0d06caSMauro Carvalho Chehab * Utility functions 400c0d06caSMauro Carvalho Chehab */ 410c0d06caSMauro Carvalho Chehab 420c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, 432c6b222cSLaurent Pinchart u8 epaddr) 440c0d06caSMauro Carvalho Chehab { 450c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep; 460c0d06caSMauro Carvalho Chehab unsigned int i; 470c0d06caSMauro Carvalho Chehab 480c0d06caSMauro Carvalho Chehab for (i = 0; i < alts->desc.bNumEndpoints; ++i) { 490c0d06caSMauro Carvalho Chehab ep = &alts->endpoint[i]; 500c0d06caSMauro Carvalho Chehab if (ep->desc.bEndpointAddress == epaddr) 510c0d06caSMauro Carvalho Chehab return ep; 520c0d06caSMauro Carvalho Chehab } 530c0d06caSMauro Carvalho Chehab 540c0d06caSMauro Carvalho Chehab return NULL; 550c0d06caSMauro Carvalho Chehab } 560c0d06caSMauro Carvalho Chehab 57ec2c23f6SAdam Goode static enum v4l2_colorspace uvc_colorspace(const u8 primaries) 580c0d06caSMauro Carvalho Chehab { 59ec2c23f6SAdam Goode static const enum v4l2_colorspace colorprimaries[] = { 60e82822faSRicardo Ribalda V4L2_COLORSPACE_SRGB, /* Unspecified */ 610c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SRGB, 620c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_470_SYSTEM_M, 630c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_470_SYSTEM_BG, 640c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SMPTE170M, 650c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SMPTE240M, 660c0d06caSMauro Carvalho Chehab }; 670c0d06caSMauro Carvalho Chehab 680c0d06caSMauro Carvalho Chehab if (primaries < ARRAY_SIZE(colorprimaries)) 690c0d06caSMauro Carvalho Chehab return colorprimaries[primaries]; 700c0d06caSMauro Carvalho Chehab 71e82822faSRicardo Ribalda return V4L2_COLORSPACE_SRGB; /* Reserved */ 72ec2c23f6SAdam Goode } 73ec2c23f6SAdam Goode 74ec2c23f6SAdam Goode static enum v4l2_xfer_func uvc_xfer_func(const u8 transfer_characteristics) 75ec2c23f6SAdam Goode { 76ec2c23f6SAdam Goode /* 77ec2c23f6SAdam Goode * V4L2 does not currently have definitions for all possible values of 78ec2c23f6SAdam Goode * UVC transfer characteristics. If v4l2_xfer_func is extended with new 79ec2c23f6SAdam Goode * values, the mapping below should be updated. 80ec2c23f6SAdam Goode * 81ec2c23f6SAdam Goode * Substitutions are taken from the mapping given for 82ec2c23f6SAdam Goode * V4L2_XFER_FUNC_DEFAULT documented in videodev2.h. 83ec2c23f6SAdam Goode */ 84ec2c23f6SAdam Goode static const enum v4l2_xfer_func xfer_funcs[] = { 85ec2c23f6SAdam Goode V4L2_XFER_FUNC_DEFAULT, /* Unspecified */ 86ec2c23f6SAdam Goode V4L2_XFER_FUNC_709, 87ec2c23f6SAdam Goode V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 M */ 88ec2c23f6SAdam Goode V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 B, G */ 89ec2c23f6SAdam Goode V4L2_XFER_FUNC_709, /* Substitution for SMPTE 170M */ 90ec2c23f6SAdam Goode V4L2_XFER_FUNC_SMPTE240M, 91ec2c23f6SAdam Goode V4L2_XFER_FUNC_NONE, 92ec2c23f6SAdam Goode V4L2_XFER_FUNC_SRGB, 93ec2c23f6SAdam Goode }; 94ec2c23f6SAdam Goode 95ec2c23f6SAdam Goode if (transfer_characteristics < ARRAY_SIZE(xfer_funcs)) 96ec2c23f6SAdam Goode return xfer_funcs[transfer_characteristics]; 97ec2c23f6SAdam Goode 98ec2c23f6SAdam Goode return V4L2_XFER_FUNC_DEFAULT; /* Reserved */ 99ec2c23f6SAdam Goode } 100ec2c23f6SAdam Goode 101ec2c23f6SAdam Goode static enum v4l2_ycbcr_encoding uvc_ycbcr_enc(const u8 matrix_coefficients) 102ec2c23f6SAdam Goode { 103ec2c23f6SAdam Goode /* 104ec2c23f6SAdam Goode * V4L2 does not currently have definitions for all possible values of 105ec2c23f6SAdam Goode * UVC matrix coefficients. If v4l2_ycbcr_encoding is extended with new 106ec2c23f6SAdam Goode * values, the mapping below should be updated. 107ec2c23f6SAdam Goode * 108ec2c23f6SAdam Goode * Substitutions are taken from the mapping given for 109ec2c23f6SAdam Goode * V4L2_YCBCR_ENC_DEFAULT documented in videodev2.h. 110ec2c23f6SAdam Goode * 111ec2c23f6SAdam Goode * FCC is assumed to be close enough to 601. 112ec2c23f6SAdam Goode */ 113ec2c23f6SAdam Goode static const enum v4l2_ycbcr_encoding ycbcr_encs[] = { 114ec2c23f6SAdam Goode V4L2_YCBCR_ENC_DEFAULT, /* Unspecified */ 115ec2c23f6SAdam Goode V4L2_YCBCR_ENC_709, 116ec2c23f6SAdam Goode V4L2_YCBCR_ENC_601, /* Substitution for FCC */ 117ec2c23f6SAdam Goode V4L2_YCBCR_ENC_601, /* Substitution for BT.470-2 B, G */ 118ec2c23f6SAdam Goode V4L2_YCBCR_ENC_601, 119ec2c23f6SAdam Goode V4L2_YCBCR_ENC_SMPTE240M, 120ec2c23f6SAdam Goode }; 121ec2c23f6SAdam Goode 122ec2c23f6SAdam Goode if (matrix_coefficients < ARRAY_SIZE(ycbcr_encs)) 123ec2c23f6SAdam Goode return ycbcr_encs[matrix_coefficients]; 124ec2c23f6SAdam Goode 125ec2c23f6SAdam Goode return V4L2_YCBCR_ENC_DEFAULT; /* Reserved */ 1260c0d06caSMauro Carvalho Chehab } 1270c0d06caSMauro Carvalho Chehab 1280c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 1290c0d06caSMauro Carvalho Chehab * Terminal and unit management 1300c0d06caSMauro Carvalho Chehab */ 1310c0d06caSMauro Carvalho Chehab 1320c0d06caSMauro Carvalho Chehab struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id) 1330c0d06caSMauro Carvalho Chehab { 1340c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 1350c0d06caSMauro Carvalho Chehab 1360c0d06caSMauro Carvalho Chehab list_for_each_entry(entity, &dev->entities, list) { 1370c0d06caSMauro Carvalho Chehab if (entity->id == id) 1380c0d06caSMauro Carvalho Chehab return entity; 1390c0d06caSMauro Carvalho Chehab } 1400c0d06caSMauro Carvalho Chehab 1410c0d06caSMauro Carvalho Chehab return NULL; 1420c0d06caSMauro Carvalho Chehab } 1430c0d06caSMauro Carvalho Chehab 1440c0d06caSMauro Carvalho Chehab static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, 1450c0d06caSMauro Carvalho Chehab int id, struct uvc_entity *entity) 1460c0d06caSMauro Carvalho Chehab { 1470c0d06caSMauro Carvalho Chehab unsigned int i; 1480c0d06caSMauro Carvalho Chehab 1490c0d06caSMauro Carvalho Chehab if (entity == NULL) 1500c0d06caSMauro Carvalho Chehab entity = list_entry(&dev->entities, struct uvc_entity, list); 1510c0d06caSMauro Carvalho Chehab 1520c0d06caSMauro Carvalho Chehab list_for_each_entry_continue(entity, &dev->entities, list) { 1530c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->bNrInPins; ++i) 1540c0d06caSMauro Carvalho Chehab if (entity->baSourceID[i] == id) 1550c0d06caSMauro Carvalho Chehab return entity; 1560c0d06caSMauro Carvalho Chehab } 1570c0d06caSMauro Carvalho Chehab 1580c0d06caSMauro Carvalho Chehab return NULL; 1590c0d06caSMauro Carvalho Chehab } 1600c0d06caSMauro Carvalho Chehab 1610c0d06caSMauro Carvalho Chehab static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id) 1620c0d06caSMauro Carvalho Chehab { 1630c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 1640c0d06caSMauro Carvalho Chehab 1650c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 1660c0d06caSMauro Carvalho Chehab if (stream->header.bTerminalLink == id) 1670c0d06caSMauro Carvalho Chehab return stream; 1680c0d06caSMauro Carvalho Chehab } 1690c0d06caSMauro Carvalho Chehab 1700c0d06caSMauro Carvalho Chehab return NULL; 1710c0d06caSMauro Carvalho Chehab } 1720c0d06caSMauro Carvalho Chehab 1730c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 174ece41454SKieran Bingham * Streaming Object Management 175ece41454SKieran Bingham */ 176ece41454SKieran Bingham 177ece41454SKieran Bingham static void uvc_stream_delete(struct uvc_streaming *stream) 178ece41454SKieran Bingham { 179b012186aSKieran Bingham if (stream->async_wq) 180b012186aSKieran Bingham destroy_workqueue(stream->async_wq); 181b012186aSKieran Bingham 182ece41454SKieran Bingham mutex_destroy(&stream->mutex); 183ece41454SKieran Bingham 184ece41454SKieran Bingham usb_put_intf(stream->intf); 185ece41454SKieran Bingham 186ece41454SKieran Bingham kfree(stream->format); 187ece41454SKieran Bingham kfree(stream->header.bmaControls); 188ece41454SKieran Bingham kfree(stream); 189ece41454SKieran Bingham } 190ece41454SKieran Bingham 191ece41454SKieran Bingham static struct uvc_streaming *uvc_stream_new(struct uvc_device *dev, 192ece41454SKieran Bingham struct usb_interface *intf) 193ece41454SKieran Bingham { 194ece41454SKieran Bingham struct uvc_streaming *stream; 195ece41454SKieran Bingham 196ece41454SKieran Bingham stream = kzalloc(sizeof(*stream), GFP_KERNEL); 197ece41454SKieran Bingham if (stream == NULL) 198ece41454SKieran Bingham return NULL; 199ece41454SKieran Bingham 200ece41454SKieran Bingham mutex_init(&stream->mutex); 201ece41454SKieran Bingham 202ece41454SKieran Bingham stream->dev = dev; 203ece41454SKieran Bingham stream->intf = usb_get_intf(intf); 204ece41454SKieran Bingham stream->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; 205ece41454SKieran Bingham 206b012186aSKieran Bingham /* Allocate a stream specific work queue for asynchronous tasks. */ 207b012186aSKieran Bingham stream->async_wq = alloc_workqueue("uvcvideo", WQ_UNBOUND | WQ_HIGHPRI, 208b012186aSKieran Bingham 0); 209b012186aSKieran Bingham if (!stream->async_wq) { 210b012186aSKieran Bingham uvc_stream_delete(stream); 211b012186aSKieran Bingham return NULL; 212b012186aSKieran Bingham } 213b012186aSKieran Bingham 214ece41454SKieran Bingham return stream; 215ece41454SKieran Bingham } 216ece41454SKieran Bingham 217ece41454SKieran Bingham /* ------------------------------------------------------------------------ 2180c0d06caSMauro Carvalho Chehab * Descriptors parsing 2190c0d06caSMauro Carvalho Chehab */ 2200c0d06caSMauro Carvalho Chehab 2210c0d06caSMauro Carvalho Chehab static int uvc_parse_format(struct uvc_device *dev, 2220c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming, struct uvc_format *format, 2232c6b222cSLaurent Pinchart u32 **intervals, unsigned char *buffer, int buflen) 2240c0d06caSMauro Carvalho Chehab { 2250c0d06caSMauro Carvalho Chehab struct usb_interface *intf = streaming->intf; 2260c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = intf->cur_altsetting; 2270c0d06caSMauro Carvalho Chehab struct uvc_format_desc *fmtdesc; 2280c0d06caSMauro Carvalho Chehab struct uvc_frame *frame; 2290c0d06caSMauro Carvalho Chehab const unsigned char *start = buffer; 230e1b78a33SPhilipp Zabel unsigned int width_multiplier = 1; 2310c0d06caSMauro Carvalho Chehab unsigned int interval; 2320c0d06caSMauro Carvalho Chehab unsigned int i, n; 2332c6b222cSLaurent Pinchart u8 ftype; 2340c0d06caSMauro Carvalho Chehab 2350c0d06caSMauro Carvalho Chehab format->type = buffer[2]; 2360c0d06caSMauro Carvalho Chehab format->index = buffer[3]; 2370c0d06caSMauro Carvalho Chehab 2380c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 2390c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 2400c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 2410c0d06caSMauro Carvalho Chehab n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28; 2420c0d06caSMauro Carvalho Chehab if (buflen < n) { 2439e56380aSJoe Perches uvc_dbg(dev, DESCR, 244ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d FORMAT error\n", 2450c0d06caSMauro Carvalho Chehab dev->udev->devnum, 2460c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 2470c0d06caSMauro Carvalho Chehab return -EINVAL; 2480c0d06caSMauro Carvalho Chehab } 2490c0d06caSMauro Carvalho Chehab 2500c0d06caSMauro Carvalho Chehab /* Find the format descriptor from its GUID. */ 2510c0d06caSMauro Carvalho Chehab fmtdesc = uvc_format_by_guid(&buffer[5]); 2520c0d06caSMauro Carvalho Chehab 2530c0d06caSMauro Carvalho Chehab if (fmtdesc != NULL) { 2540c0d06caSMauro Carvalho Chehab format->fcc = fmtdesc->fcc; 2550c0d06caSMauro Carvalho Chehab } else { 25669df0954SRicardo Ribalda dev_info(&streaming->intf->dev, 25769df0954SRicardo Ribalda "Unknown video format %pUl\n", &buffer[5]); 2580c0d06caSMauro Carvalho Chehab format->fcc = 0; 2590c0d06caSMauro Carvalho Chehab } 2600c0d06caSMauro Carvalho Chehab 2610c0d06caSMauro Carvalho Chehab format->bpp = buffer[21]; 262e1b78a33SPhilipp Zabel 263699b9a86SLaurent Pinchart /* 264699b9a86SLaurent Pinchart * Some devices report a format that doesn't match what they 265e1b78a33SPhilipp Zabel * really send. 266e1b78a33SPhilipp Zabel */ 267e1b78a33SPhilipp Zabel if (dev->quirks & UVC_QUIRK_FORCE_Y8) { 268e1b78a33SPhilipp Zabel if (format->fcc == V4L2_PIX_FMT_YUYV) { 269e1b78a33SPhilipp Zabel format->fcc = V4L2_PIX_FMT_GREY; 270e1b78a33SPhilipp Zabel format->bpp = 8; 271e1b78a33SPhilipp Zabel width_multiplier = 2; 272e1b78a33SPhilipp Zabel } 273e1b78a33SPhilipp Zabel } 274e1b78a33SPhilipp Zabel 2751dd2e8f9SSergey Zakharchenko /* Some devices report bpp that doesn't match the format. */ 2761dd2e8f9SSergey Zakharchenko if (dev->quirks & UVC_QUIRK_FORCE_BPP) { 2771dd2e8f9SSergey Zakharchenko const struct v4l2_format_info *info = 2781dd2e8f9SSergey Zakharchenko v4l2_format_info(format->fcc); 2791dd2e8f9SSergey Zakharchenko 2801dd2e8f9SSergey Zakharchenko if (info) { 2811dd2e8f9SSergey Zakharchenko unsigned int div = info->hdiv * info->vdiv; 2821dd2e8f9SSergey Zakharchenko 2831dd2e8f9SSergey Zakharchenko n = info->bpp[0] * div; 2841dd2e8f9SSergey Zakharchenko for (i = 1; i < info->comp_planes; i++) 2851dd2e8f9SSergey Zakharchenko n += info->bpp[i]; 2861dd2e8f9SSergey Zakharchenko 2871dd2e8f9SSergey Zakharchenko format->bpp = DIV_ROUND_UP(8 * n, div); 2881dd2e8f9SSergey Zakharchenko } 2891dd2e8f9SSergey Zakharchenko } 2901dd2e8f9SSergey Zakharchenko 2910c0d06caSMauro Carvalho Chehab if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) { 2920c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_UNCOMPRESSED; 2930c0d06caSMauro Carvalho Chehab } else { 2940c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_FRAME_BASED; 2950c0d06caSMauro Carvalho Chehab if (buffer[27]) 2960c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED; 2970c0d06caSMauro Carvalho Chehab } 2980c0d06caSMauro Carvalho Chehab break; 2990c0d06caSMauro Carvalho Chehab 3000c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 3010c0d06caSMauro Carvalho Chehab if (buflen < 11) { 3029e56380aSJoe Perches uvc_dbg(dev, DESCR, 303ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d FORMAT error\n", 3040c0d06caSMauro Carvalho Chehab dev->udev->devnum, 3050c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 3060c0d06caSMauro Carvalho Chehab return -EINVAL; 3070c0d06caSMauro Carvalho Chehab } 3080c0d06caSMauro Carvalho Chehab 3090c0d06caSMauro Carvalho Chehab format->fcc = V4L2_PIX_FMT_MJPEG; 3100c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED; 3110c0d06caSMauro Carvalho Chehab format->bpp = 0; 3120c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_MJPEG; 3130c0d06caSMauro Carvalho Chehab break; 3140c0d06caSMauro Carvalho Chehab 3150c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 3160c0d06caSMauro Carvalho Chehab if (buflen < 9) { 3179e56380aSJoe Perches uvc_dbg(dev, DESCR, 318ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d FORMAT error\n", 3190c0d06caSMauro Carvalho Chehab dev->udev->devnum, 3200c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 3210c0d06caSMauro Carvalho Chehab return -EINVAL; 3220c0d06caSMauro Carvalho Chehab } 3230c0d06caSMauro Carvalho Chehab 32450459f10SLaurent Pinchart if ((buffer[8] & 0x7f) > 2) { 3259e56380aSJoe Perches uvc_dbg(dev, DESCR, 326ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d: unknown DV format %u\n", 3270c0d06caSMauro Carvalho Chehab dev->udev->devnum, 3280c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, buffer[8]); 3290c0d06caSMauro Carvalho Chehab return -EINVAL; 3300c0d06caSMauro Carvalho Chehab } 3310c0d06caSMauro Carvalho Chehab 3320c0d06caSMauro Carvalho Chehab format->fcc = V4L2_PIX_FMT_DV; 3330c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM; 3340c0d06caSMauro Carvalho Chehab format->bpp = 0; 3350c0d06caSMauro Carvalho Chehab ftype = 0; 3360c0d06caSMauro Carvalho Chehab 3370c0d06caSMauro Carvalho Chehab /* Create a dummy frame descriptor. */ 3380c0d06caSMauro Carvalho Chehab frame = &format->frame[0]; 339f14d4988SLaurent Pinchart memset(&format->frame[0], 0, sizeof(format->frame[0])); 3400c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = 1; 3410c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 1; 3420c0d06caSMauro Carvalho Chehab frame->dwFrameInterval = *intervals; 3430c0d06caSMauro Carvalho Chehab *(*intervals)++ = 1; 3440c0d06caSMauro Carvalho Chehab format->nframes = 1; 3450c0d06caSMauro Carvalho Chehab break; 3460c0d06caSMauro Carvalho Chehab 3470c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MPEG2TS: 3480c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_STREAM_BASED: 3490c0d06caSMauro Carvalho Chehab /* Not supported yet. */ 3500c0d06caSMauro Carvalho Chehab default: 3519e56380aSJoe Perches uvc_dbg(dev, DESCR, 352ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d unsupported format %u\n", 3530c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber, 3540c0d06caSMauro Carvalho Chehab buffer[2]); 3550c0d06caSMauro Carvalho Chehab return -EINVAL; 3560c0d06caSMauro Carvalho Chehab } 3570c0d06caSMauro Carvalho Chehab 35850459f10SLaurent Pinchart uvc_dbg(dev, DESCR, "Found format %p4cc", &format->fcc); 3590c0d06caSMauro Carvalho Chehab 3600c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 3610c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 3620c0d06caSMauro Carvalho Chehab 363699b9a86SLaurent Pinchart /* 364699b9a86SLaurent Pinchart * Parse the frame descriptors. Only uncompressed, MJPEG and frame 3650c0d06caSMauro Carvalho Chehab * based formats have frame descriptors. 3660c0d06caSMauro Carvalho Chehab */ 3670c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 3680c0d06caSMauro Carvalho Chehab buffer[2] == ftype) { 3690c0d06caSMauro Carvalho Chehab frame = &format->frame[format->nframes]; 3700c0d06caSMauro Carvalho Chehab if (ftype != UVC_VS_FRAME_FRAME_BASED) 3710c0d06caSMauro Carvalho Chehab n = buflen > 25 ? buffer[25] : 0; 3720c0d06caSMauro Carvalho Chehab else 3730c0d06caSMauro Carvalho Chehab n = buflen > 21 ? buffer[21] : 0; 3740c0d06caSMauro Carvalho Chehab 3750c0d06caSMauro Carvalho Chehab n = n ? n : 3; 3760c0d06caSMauro Carvalho Chehab 3770c0d06caSMauro Carvalho Chehab if (buflen < 26 + 4*n) { 3789e56380aSJoe Perches uvc_dbg(dev, DESCR, 379ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d FRAME error\n", 380ed4c5fa4SRicardo Ribalda dev->udev->devnum, 3810c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 3820c0d06caSMauro Carvalho Chehab return -EINVAL; 3830c0d06caSMauro Carvalho Chehab } 3840c0d06caSMauro Carvalho Chehab 3850c0d06caSMauro Carvalho Chehab frame->bFrameIndex = buffer[3]; 3860c0d06caSMauro Carvalho Chehab frame->bmCapabilities = buffer[4]; 387e1b78a33SPhilipp Zabel frame->wWidth = get_unaligned_le16(&buffer[5]) 388e1b78a33SPhilipp Zabel * width_multiplier; 3890c0d06caSMauro Carvalho Chehab frame->wHeight = get_unaligned_le16(&buffer[7]); 3900c0d06caSMauro Carvalho Chehab frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); 3910c0d06caSMauro Carvalho Chehab frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); 3920c0d06caSMauro Carvalho Chehab if (ftype != UVC_VS_FRAME_FRAME_BASED) { 3930c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = 3940c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[17]); 3950c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 3960c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[21]); 3970c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = buffer[25]; 3980c0d06caSMauro Carvalho Chehab } else { 3990c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = 0; 4000c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 4010c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[17]); 4020c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = buffer[21]; 4030c0d06caSMauro Carvalho Chehab } 4040c0d06caSMauro Carvalho Chehab frame->dwFrameInterval = *intervals; 4050c0d06caSMauro Carvalho Chehab 406699b9a86SLaurent Pinchart /* 407699b9a86SLaurent Pinchart * Several UVC chipsets screw up dwMaxVideoFrameBufferSize 4080c0d06caSMauro Carvalho Chehab * completely. Observed behaviours range from setting the 4090c0d06caSMauro Carvalho Chehab * value to 1.1x the actual frame size to hardwiring the 4100c0d06caSMauro Carvalho Chehab * 16 low bits to 0. This results in a higher than necessary 4110c0d06caSMauro Carvalho Chehab * memory usage as well as a wrong image size information. For 4120c0d06caSMauro Carvalho Chehab * uncompressed formats this can be fixed by computing the 4130c0d06caSMauro Carvalho Chehab * value from the frame size. 4140c0d06caSMauro Carvalho Chehab */ 4150c0d06caSMauro Carvalho Chehab if (!(format->flags & UVC_FMT_FLAG_COMPRESSED)) 4160c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = format->bpp 4170c0d06caSMauro Carvalho Chehab * frame->wWidth * frame->wHeight / 8; 4180c0d06caSMauro Carvalho Chehab 419699b9a86SLaurent Pinchart /* 420699b9a86SLaurent Pinchart * Some bogus devices report dwMinFrameInterval equal to 4210c0d06caSMauro Carvalho Chehab * dwMaxFrameInterval and have dwFrameIntervalStep set to 4220c0d06caSMauro Carvalho Chehab * zero. Setting all null intervals to 1 fixes the problem and 4230c0d06caSMauro Carvalho Chehab * some other divisions by zero that could happen. 4240c0d06caSMauro Carvalho Chehab */ 4250c0d06caSMauro Carvalho Chehab for (i = 0; i < n; ++i) { 4260c0d06caSMauro Carvalho Chehab interval = get_unaligned_le32(&buffer[26+4*i]); 4270c0d06caSMauro Carvalho Chehab *(*intervals)++ = interval ? interval : 1; 4280c0d06caSMauro Carvalho Chehab } 4290c0d06caSMauro Carvalho Chehab 430699b9a86SLaurent Pinchart /* 431699b9a86SLaurent Pinchart * Make sure that the default frame interval stays between 4320c0d06caSMauro Carvalho Chehab * the boundaries. 4330c0d06caSMauro Carvalho Chehab */ 4340c0d06caSMauro Carvalho Chehab n -= frame->bFrameIntervalType ? 1 : 2; 4350c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 4360c0d06caSMauro Carvalho Chehab min(frame->dwFrameInterval[n], 4370c0d06caSMauro Carvalho Chehab max(frame->dwFrameInterval[0], 4380c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval)); 4390c0d06caSMauro Carvalho Chehab 4400c0d06caSMauro Carvalho Chehab if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) { 4410c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = 1; 4420c0d06caSMauro Carvalho Chehab frame->dwFrameInterval[0] = 4430c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval; 4440c0d06caSMauro Carvalho Chehab } 4450c0d06caSMauro Carvalho Chehab 4469e56380aSJoe Perches uvc_dbg(dev, DESCR, "- %ux%u (%u.%u fps)\n", 4470c0d06caSMauro Carvalho Chehab frame->wWidth, frame->wHeight, 4480c0d06caSMauro Carvalho Chehab 10000000 / frame->dwDefaultFrameInterval, 4490c0d06caSMauro Carvalho Chehab (100000000 / frame->dwDefaultFrameInterval) % 10); 4500c0d06caSMauro Carvalho Chehab 4510c0d06caSMauro Carvalho Chehab format->nframes++; 4520c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 4530c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 4540c0d06caSMauro Carvalho Chehab } 4550c0d06caSMauro Carvalho Chehab 4560c0d06caSMauro Carvalho Chehab if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 4570c0d06caSMauro Carvalho Chehab buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { 4580c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 4590c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 4600c0d06caSMauro Carvalho Chehab } 4610c0d06caSMauro Carvalho Chehab 4620c0d06caSMauro Carvalho Chehab if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 4630c0d06caSMauro Carvalho Chehab buffer[2] == UVC_VS_COLORFORMAT) { 4640c0d06caSMauro Carvalho Chehab if (buflen < 6) { 4659e56380aSJoe Perches uvc_dbg(dev, DESCR, 466ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d COLORFORMAT error\n", 4670c0d06caSMauro Carvalho Chehab dev->udev->devnum, 4680c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 4690c0d06caSMauro Carvalho Chehab return -EINVAL; 4700c0d06caSMauro Carvalho Chehab } 4710c0d06caSMauro Carvalho Chehab 4720c0d06caSMauro Carvalho Chehab format->colorspace = uvc_colorspace(buffer[3]); 473ec2c23f6SAdam Goode format->xfer_func = uvc_xfer_func(buffer[4]); 474ec2c23f6SAdam Goode format->ycbcr_enc = uvc_ycbcr_enc(buffer[5]); 4750c0d06caSMauro Carvalho Chehab 4760c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 4770c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 478e82822faSRicardo Ribalda } else { 479e82822faSRicardo Ribalda format->colorspace = V4L2_COLORSPACE_SRGB; 4800c0d06caSMauro Carvalho Chehab } 4810c0d06caSMauro Carvalho Chehab 4820c0d06caSMauro Carvalho Chehab return buffer - start; 4830c0d06caSMauro Carvalho Chehab } 4840c0d06caSMauro Carvalho Chehab 4850c0d06caSMauro Carvalho Chehab static int uvc_parse_streaming(struct uvc_device *dev, 4860c0d06caSMauro Carvalho Chehab struct usb_interface *intf) 4870c0d06caSMauro Carvalho Chehab { 4880c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming = NULL; 4890c0d06caSMauro Carvalho Chehab struct uvc_format *format; 4900c0d06caSMauro Carvalho Chehab struct uvc_frame *frame; 4910c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = &intf->altsetting[0]; 4920c0d06caSMauro Carvalho Chehab unsigned char *_buffer, *buffer = alts->extra; 4930c0d06caSMauro Carvalho Chehab int _buflen, buflen = alts->extralen; 4940c0d06caSMauro Carvalho Chehab unsigned int nformats = 0, nframes = 0, nintervals = 0; 4950c0d06caSMauro Carvalho Chehab unsigned int size, i, n, p; 4962c6b222cSLaurent Pinchart u32 *interval; 4972c6b222cSLaurent Pinchart u16 psize; 4980c0d06caSMauro Carvalho Chehab int ret = -EINVAL; 4990c0d06caSMauro Carvalho Chehab 5000c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass 5010c0d06caSMauro Carvalho Chehab != UVC_SC_VIDEOSTREAMING) { 5029e56380aSJoe Perches uvc_dbg(dev, DESCR, 503ed4c5fa4SRicardo Ribalda "device %d interface %d isn't a video streaming interface\n", 504ed4c5fa4SRicardo Ribalda dev->udev->devnum, 5050c0d06caSMauro Carvalho Chehab intf->altsetting[0].desc.bInterfaceNumber); 5060c0d06caSMauro Carvalho Chehab return -EINVAL; 5070c0d06caSMauro Carvalho Chehab } 5080c0d06caSMauro Carvalho Chehab 5090c0d06caSMauro Carvalho Chehab if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) { 5109e56380aSJoe Perches uvc_dbg(dev, DESCR, 511ed4c5fa4SRicardo Ribalda "device %d interface %d is already claimed\n", 512ed4c5fa4SRicardo Ribalda dev->udev->devnum, 5130c0d06caSMauro Carvalho Chehab intf->altsetting[0].desc.bInterfaceNumber); 5140c0d06caSMauro Carvalho Chehab return -EINVAL; 5150c0d06caSMauro Carvalho Chehab } 5160c0d06caSMauro Carvalho Chehab 517ece41454SKieran Bingham streaming = uvc_stream_new(dev, intf); 5180c0d06caSMauro Carvalho Chehab if (streaming == NULL) { 5190c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, intf); 520ece41454SKieran Bingham return -ENOMEM; 5210c0d06caSMauro Carvalho Chehab } 5220c0d06caSMauro Carvalho Chehab 523699b9a86SLaurent Pinchart /* 524699b9a86SLaurent Pinchart * The Pico iMage webcam has its class-specific interface descriptors 5250c0d06caSMauro Carvalho Chehab * after the endpoint descriptors. 5260c0d06caSMauro Carvalho Chehab */ 5270c0d06caSMauro Carvalho Chehab if (buflen == 0) { 5280c0d06caSMauro Carvalho Chehab for (i = 0; i < alts->desc.bNumEndpoints; ++i) { 5290c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep = &alts->endpoint[i]; 5300c0d06caSMauro Carvalho Chehab 5310c0d06caSMauro Carvalho Chehab if (ep->extralen == 0) 5320c0d06caSMauro Carvalho Chehab continue; 5330c0d06caSMauro Carvalho Chehab 5340c0d06caSMauro Carvalho Chehab if (ep->extralen > 2 && 5350c0d06caSMauro Carvalho Chehab ep->extra[1] == USB_DT_CS_INTERFACE) { 5369e56380aSJoe Perches uvc_dbg(dev, DESCR, 5379e56380aSJoe Perches "trying extra data from endpoint %u\n", 538ed4c5fa4SRicardo Ribalda i); 5390c0d06caSMauro Carvalho Chehab buffer = alts->endpoint[i].extra; 5400c0d06caSMauro Carvalho Chehab buflen = alts->endpoint[i].extralen; 5410c0d06caSMauro Carvalho Chehab break; 5420c0d06caSMauro Carvalho Chehab } 5430c0d06caSMauro Carvalho Chehab } 5440c0d06caSMauro Carvalho Chehab } 5450c0d06caSMauro Carvalho Chehab 5460c0d06caSMauro Carvalho Chehab /* Skip the standard interface descriptors. */ 5470c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) { 5480c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 5490c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 5500c0d06caSMauro Carvalho Chehab } 5510c0d06caSMauro Carvalho Chehab 5520c0d06caSMauro Carvalho Chehab if (buflen <= 2) { 5539e56380aSJoe Perches uvc_dbg(dev, DESCR, 5549e56380aSJoe Perches "no class-specific streaming interface descriptors found\n"); 5550c0d06caSMauro Carvalho Chehab goto error; 5560c0d06caSMauro Carvalho Chehab } 5570c0d06caSMauro Carvalho Chehab 5580c0d06caSMauro Carvalho Chehab /* Parse the header descriptor. */ 5590c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 5600c0d06caSMauro Carvalho Chehab case UVC_VS_OUTPUT_HEADER: 5610c0d06caSMauro Carvalho Chehab streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 5620c0d06caSMauro Carvalho Chehab size = 9; 5630c0d06caSMauro Carvalho Chehab break; 5640c0d06caSMauro Carvalho Chehab 5650c0d06caSMauro Carvalho Chehab case UVC_VS_INPUT_HEADER: 5660c0d06caSMauro Carvalho Chehab streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 5670c0d06caSMauro Carvalho Chehab size = 13; 5680c0d06caSMauro Carvalho Chehab break; 5690c0d06caSMauro Carvalho Chehab 5700c0d06caSMauro Carvalho Chehab default: 5719e56380aSJoe Perches uvc_dbg(dev, DESCR, 5729e56380aSJoe Perches "device %d videostreaming interface %d HEADER descriptor not found\n", 573ed4c5fa4SRicardo Ribalda dev->udev->devnum, alts->desc.bInterfaceNumber); 5740c0d06caSMauro Carvalho Chehab goto error; 5750c0d06caSMauro Carvalho Chehab } 5760c0d06caSMauro Carvalho Chehab 5770c0d06caSMauro Carvalho Chehab p = buflen >= 4 ? buffer[3] : 0; 5780c0d06caSMauro Carvalho Chehab n = buflen >= size ? buffer[size-1] : 0; 5790c0d06caSMauro Carvalho Chehab 5800c0d06caSMauro Carvalho Chehab if (buflen < size + p*n) { 5819e56380aSJoe Perches uvc_dbg(dev, DESCR, 5829e56380aSJoe Perches "device %d videostreaming interface %d HEADER descriptor is invalid\n", 5830c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber); 5840c0d06caSMauro Carvalho Chehab goto error; 5850c0d06caSMauro Carvalho Chehab } 5860c0d06caSMauro Carvalho Chehab 5870c0d06caSMauro Carvalho Chehab streaming->header.bNumFormats = p; 5880c0d06caSMauro Carvalho Chehab streaming->header.bEndpointAddress = buffer[6]; 5890c0d06caSMauro Carvalho Chehab if (buffer[2] == UVC_VS_INPUT_HEADER) { 5900c0d06caSMauro Carvalho Chehab streaming->header.bmInfo = buffer[7]; 5910c0d06caSMauro Carvalho Chehab streaming->header.bTerminalLink = buffer[8]; 5920c0d06caSMauro Carvalho Chehab streaming->header.bStillCaptureMethod = buffer[9]; 5930c0d06caSMauro Carvalho Chehab streaming->header.bTriggerSupport = buffer[10]; 5940c0d06caSMauro Carvalho Chehab streaming->header.bTriggerUsage = buffer[11]; 5950c0d06caSMauro Carvalho Chehab } else { 5960c0d06caSMauro Carvalho Chehab streaming->header.bTerminalLink = buffer[7]; 5970c0d06caSMauro Carvalho Chehab } 5980c0d06caSMauro Carvalho Chehab streaming->header.bControlSize = n; 5990c0d06caSMauro Carvalho Chehab 6000c0d06caSMauro Carvalho Chehab streaming->header.bmaControls = kmemdup(&buffer[size], p * n, 6010c0d06caSMauro Carvalho Chehab GFP_KERNEL); 6020c0d06caSMauro Carvalho Chehab if (streaming->header.bmaControls == NULL) { 6030c0d06caSMauro Carvalho Chehab ret = -ENOMEM; 6040c0d06caSMauro Carvalho Chehab goto error; 6050c0d06caSMauro Carvalho Chehab } 6060c0d06caSMauro Carvalho Chehab 6070c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 6080c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 6090c0d06caSMauro Carvalho Chehab 6100c0d06caSMauro Carvalho Chehab _buffer = buffer; 6110c0d06caSMauro Carvalho Chehab _buflen = buflen; 6120c0d06caSMauro Carvalho Chehab 6130c0d06caSMauro Carvalho Chehab /* Count the format and frame descriptors. */ 6140c0d06caSMauro Carvalho Chehab while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) { 6150c0d06caSMauro Carvalho Chehab switch (_buffer[2]) { 6160c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 6170c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 6180c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 6190c0d06caSMauro Carvalho Chehab nformats++; 6200c0d06caSMauro Carvalho Chehab break; 6210c0d06caSMauro Carvalho Chehab 6220c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 623699b9a86SLaurent Pinchart /* 624699b9a86SLaurent Pinchart * DV format has no frame descriptor. We will create a 6250c0d06caSMauro Carvalho Chehab * dummy frame descriptor with a dummy frame interval. 6260c0d06caSMauro Carvalho Chehab */ 6270c0d06caSMauro Carvalho Chehab nformats++; 6280c0d06caSMauro Carvalho Chehab nframes++; 6290c0d06caSMauro Carvalho Chehab nintervals++; 6300c0d06caSMauro Carvalho Chehab break; 6310c0d06caSMauro Carvalho Chehab 6320c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MPEG2TS: 6330c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_STREAM_BASED: 6349e56380aSJoe Perches uvc_dbg(dev, DESCR, 6359e56380aSJoe Perches "device %d videostreaming interface %d FORMAT %u is not supported\n", 6360c0d06caSMauro Carvalho Chehab dev->udev->devnum, 6370c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, _buffer[2]); 6380c0d06caSMauro Carvalho Chehab break; 6390c0d06caSMauro Carvalho Chehab 6400c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_UNCOMPRESSED: 6410c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_MJPEG: 6420c0d06caSMauro Carvalho Chehab nframes++; 6430c0d06caSMauro Carvalho Chehab if (_buflen > 25) 6440c0d06caSMauro Carvalho Chehab nintervals += _buffer[25] ? _buffer[25] : 3; 6450c0d06caSMauro Carvalho Chehab break; 6460c0d06caSMauro Carvalho Chehab 6470c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_FRAME_BASED: 6480c0d06caSMauro Carvalho Chehab nframes++; 6490c0d06caSMauro Carvalho Chehab if (_buflen > 21) 6500c0d06caSMauro Carvalho Chehab nintervals += _buffer[21] ? _buffer[21] : 3; 6510c0d06caSMauro Carvalho Chehab break; 6520c0d06caSMauro Carvalho Chehab } 6530c0d06caSMauro Carvalho Chehab 6540c0d06caSMauro Carvalho Chehab _buflen -= _buffer[0]; 6550c0d06caSMauro Carvalho Chehab _buffer += _buffer[0]; 6560c0d06caSMauro Carvalho Chehab } 6570c0d06caSMauro Carvalho Chehab 6580c0d06caSMauro Carvalho Chehab if (nformats == 0) { 6599e56380aSJoe Perches uvc_dbg(dev, DESCR, 6609e56380aSJoe Perches "device %d videostreaming interface %d has no supported formats defined\n", 6610c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber); 6620c0d06caSMauro Carvalho Chehab goto error; 6630c0d06caSMauro Carvalho Chehab } 6640c0d06caSMauro Carvalho Chehab 665f14d4988SLaurent Pinchart size = nformats * sizeof(*format) + nframes * sizeof(*frame) 666f14d4988SLaurent Pinchart + nintervals * sizeof(*interval); 6670c0d06caSMauro Carvalho Chehab format = kzalloc(size, GFP_KERNEL); 6680c0d06caSMauro Carvalho Chehab if (format == NULL) { 6690c0d06caSMauro Carvalho Chehab ret = -ENOMEM; 6700c0d06caSMauro Carvalho Chehab goto error; 6710c0d06caSMauro Carvalho Chehab } 6720c0d06caSMauro Carvalho Chehab 6730c0d06caSMauro Carvalho Chehab frame = (struct uvc_frame *)&format[nformats]; 6742c6b222cSLaurent Pinchart interval = (u32 *)&frame[nframes]; 6750c0d06caSMauro Carvalho Chehab 6760c0d06caSMauro Carvalho Chehab streaming->format = format; 6770c0d06caSMauro Carvalho Chehab streaming->nformats = nformats; 6780c0d06caSMauro Carvalho Chehab 6790c0d06caSMauro Carvalho Chehab /* Parse the format descriptors. */ 6800c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) { 6810c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 6820c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 6830c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 6840c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 6850c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 6860c0d06caSMauro Carvalho Chehab format->frame = frame; 6870c0d06caSMauro Carvalho Chehab ret = uvc_parse_format(dev, streaming, format, 6880c0d06caSMauro Carvalho Chehab &interval, buffer, buflen); 6890c0d06caSMauro Carvalho Chehab if (ret < 0) 6900c0d06caSMauro Carvalho Chehab goto error; 6910c0d06caSMauro Carvalho Chehab 6920c0d06caSMauro Carvalho Chehab frame += format->nframes; 6930c0d06caSMauro Carvalho Chehab format++; 6940c0d06caSMauro Carvalho Chehab 6950c0d06caSMauro Carvalho Chehab buflen -= ret; 6960c0d06caSMauro Carvalho Chehab buffer += ret; 6970c0d06caSMauro Carvalho Chehab continue; 6980c0d06caSMauro Carvalho Chehab 6990c0d06caSMauro Carvalho Chehab default: 7000c0d06caSMauro Carvalho Chehab break; 7010c0d06caSMauro Carvalho Chehab } 7020c0d06caSMauro Carvalho Chehab 7030c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 7040c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 7050c0d06caSMauro Carvalho Chehab } 7060c0d06caSMauro Carvalho Chehab 7070c0d06caSMauro Carvalho Chehab if (buflen) 7089e56380aSJoe Perches uvc_dbg(dev, DESCR, 7099e56380aSJoe Perches "device %d videostreaming interface %d has %u bytes of trailing descriptor garbage\n", 7109e56380aSJoe Perches dev->udev->devnum, alts->desc.bInterfaceNumber, buflen); 7110c0d06caSMauro Carvalho Chehab 7120c0d06caSMauro Carvalho Chehab /* Parse the alternate settings to find the maximum bandwidth. */ 7130c0d06caSMauro Carvalho Chehab for (i = 0; i < intf->num_altsetting; ++i) { 7140c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep; 7152a8c1952SPedro Guilherme Siqueira Moreira 7160c0d06caSMauro Carvalho Chehab alts = &intf->altsetting[i]; 7170c0d06caSMauro Carvalho Chehab ep = uvc_find_endpoint(alts, 7180c0d06caSMauro Carvalho Chehab streaming->header.bEndpointAddress); 7190c0d06caSMauro Carvalho Chehab if (ep == NULL) 7200c0d06caSMauro Carvalho Chehab continue; 7215b9c75c7SRicardo Ribalda psize = uvc_endpoint_max_bpi(dev->udev, ep); 7220c0d06caSMauro Carvalho Chehab if (psize > streaming->maxpsize) 7230c0d06caSMauro Carvalho Chehab streaming->maxpsize = psize; 7240c0d06caSMauro Carvalho Chehab } 7250c0d06caSMauro Carvalho Chehab 7260c0d06caSMauro Carvalho Chehab list_add_tail(&streaming->list, &dev->streams); 7270c0d06caSMauro Carvalho Chehab return 0; 7280c0d06caSMauro Carvalho Chehab 7290c0d06caSMauro Carvalho Chehab error: 7300c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, intf); 731ece41454SKieran Bingham uvc_stream_delete(streaming); 7320c0d06caSMauro Carvalho Chehab return ret; 7330c0d06caSMauro Carvalho Chehab } 7340c0d06caSMauro Carvalho Chehab 735351509c6SRicardo Ribalda static const u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA; 7362886477fSRicardo Ribalda static const u8 uvc_gpio_guid[16] = UVC_GUID_EXT_GPIO_CONTROLLER; 737351509c6SRicardo Ribalda static const u8 uvc_media_transport_input_guid[16] = 738351509c6SRicardo Ribalda UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT; 739351509c6SRicardo Ribalda static const u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING; 740351509c6SRicardo Ribalda 741cae79e50SRicardo Ribalda static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id, 7420c0d06caSMauro Carvalho Chehab unsigned int num_pads, unsigned int extra_size) 7430c0d06caSMauro Carvalho Chehab { 7440c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 7450c0d06caSMauro Carvalho Chehab unsigned int num_inputs; 7460c0d06caSMauro Carvalho Chehab unsigned int size; 7470c0d06caSMauro Carvalho Chehab unsigned int i; 7480c0d06caSMauro Carvalho Chehab 74989dd34caSNadav Amit extra_size = roundup(extra_size, sizeof(*entity->pads)); 7507532dad6SRicardo Ribalda if (num_pads) 7517532dad6SRicardo Ribalda num_inputs = type & UVC_TERM_OUTPUT ? num_pads : num_pads - 1; 7527532dad6SRicardo Ribalda else 7537532dad6SRicardo Ribalda num_inputs = 0; 7540c0d06caSMauro Carvalho Chehab size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads 7550c0d06caSMauro Carvalho Chehab + num_inputs; 7560c0d06caSMauro Carvalho Chehab entity = kzalloc(size, GFP_KERNEL); 7570c0d06caSMauro Carvalho Chehab if (entity == NULL) 7580c0d06caSMauro Carvalho Chehab return NULL; 7590c0d06caSMauro Carvalho Chehab 7600c0d06caSMauro Carvalho Chehab entity->id = id; 7610c0d06caSMauro Carvalho Chehab entity->type = type; 7620c0d06caSMauro Carvalho Chehab 763351509c6SRicardo Ribalda /* 764351509c6SRicardo Ribalda * Set the GUID for standard entity types. For extension units, the GUID 765351509c6SRicardo Ribalda * is initialized by the caller. 766351509c6SRicardo Ribalda */ 767351509c6SRicardo Ribalda switch (type) { 7682886477fSRicardo Ribalda case UVC_EXT_GPIO_UNIT: 7692886477fSRicardo Ribalda memcpy(entity->guid, uvc_gpio_guid, 16); 7702886477fSRicardo Ribalda break; 771351509c6SRicardo Ribalda case UVC_ITT_CAMERA: 772351509c6SRicardo Ribalda memcpy(entity->guid, uvc_camera_guid, 16); 773351509c6SRicardo Ribalda break; 774351509c6SRicardo Ribalda case UVC_ITT_MEDIA_TRANSPORT_INPUT: 775351509c6SRicardo Ribalda memcpy(entity->guid, uvc_media_transport_input_guid, 16); 776351509c6SRicardo Ribalda break; 777351509c6SRicardo Ribalda case UVC_VC_PROCESSING_UNIT: 778351509c6SRicardo Ribalda memcpy(entity->guid, uvc_processing_guid, 16); 779351509c6SRicardo Ribalda break; 780351509c6SRicardo Ribalda } 781351509c6SRicardo Ribalda 7820c0d06caSMauro Carvalho Chehab entity->num_links = 0; 7830c0d06caSMauro Carvalho Chehab entity->num_pads = num_pads; 7840c0d06caSMauro Carvalho Chehab entity->pads = ((void *)(entity + 1)) + extra_size; 7850c0d06caSMauro Carvalho Chehab 7860c0d06caSMauro Carvalho Chehab for (i = 0; i < num_inputs; ++i) 7870c0d06caSMauro Carvalho Chehab entity->pads[i].flags = MEDIA_PAD_FL_SINK; 7887532dad6SRicardo Ribalda if (!UVC_ENTITY_IS_OTERM(entity) && num_pads) 7890c0d06caSMauro Carvalho Chehab entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE; 7900c0d06caSMauro Carvalho Chehab 7910c0d06caSMauro Carvalho Chehab entity->bNrInPins = num_inputs; 7922c6b222cSLaurent Pinchart entity->baSourceID = (u8 *)(&entity->pads[num_pads]); 7930c0d06caSMauro Carvalho Chehab 7940c0d06caSMauro Carvalho Chehab return entity; 7950c0d06caSMauro Carvalho Chehab } 7960c0d06caSMauro Carvalho Chehab 7970c0d06caSMauro Carvalho Chehab /* Parse vendor-specific extensions. */ 7980c0d06caSMauro Carvalho Chehab static int uvc_parse_vendor_control(struct uvc_device *dev, 7990c0d06caSMauro Carvalho Chehab const unsigned char *buffer, int buflen) 8000c0d06caSMauro Carvalho Chehab { 8010c0d06caSMauro Carvalho Chehab struct usb_device *udev = dev->udev; 8020c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 8030c0d06caSMauro Carvalho Chehab struct uvc_entity *unit; 8040c0d06caSMauro Carvalho Chehab unsigned int n, p; 8050c0d06caSMauro Carvalho Chehab int handled = 0; 8060c0d06caSMauro Carvalho Chehab 8070c0d06caSMauro Carvalho Chehab switch (le16_to_cpu(dev->udev->descriptor.idVendor)) { 8080c0d06caSMauro Carvalho Chehab case 0x046d: /* Logitech */ 8090c0d06caSMauro Carvalho Chehab if (buffer[1] != 0x41 || buffer[2] != 0x01) 8100c0d06caSMauro Carvalho Chehab break; 8110c0d06caSMauro Carvalho Chehab 812699b9a86SLaurent Pinchart /* 813699b9a86SLaurent Pinchart * Logitech implements several vendor specific functions 8140c0d06caSMauro Carvalho Chehab * through vendor specific extension units (LXU). 8150c0d06caSMauro Carvalho Chehab * 8160c0d06caSMauro Carvalho Chehab * The LXU descriptors are similar to XU descriptors 8170c0d06caSMauro Carvalho Chehab * (see "USB Device Video Class for Video Devices", section 8180c0d06caSMauro Carvalho Chehab * 3.7.2.6 "Extension Unit Descriptor") with the following 8190c0d06caSMauro Carvalho Chehab * differences: 8200c0d06caSMauro Carvalho Chehab * 8210c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 8220c0d06caSMauro Carvalho Chehab * 0 bLength 1 Number 8230c0d06caSMauro Carvalho Chehab * Size of this descriptor, in bytes: 24+p+n*2 8240c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 8250c0d06caSMauro Carvalho Chehab * 23+p+n bmControlsType N Bitmap 8260c0d06caSMauro Carvalho Chehab * Individual bits in the set are defined: 8270c0d06caSMauro Carvalho Chehab * 0: Absolute 8280c0d06caSMauro Carvalho Chehab * 1: Relative 8290c0d06caSMauro Carvalho Chehab * 8300c0d06caSMauro Carvalho Chehab * This bitset is mapped exactly the same as bmControls. 8310c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 8320c0d06caSMauro Carvalho Chehab * 23+p+n*2 bReserved 1 Boolean 8330c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 8340c0d06caSMauro Carvalho Chehab * 24+p+n*2 iExtension 1 Index 8350c0d06caSMauro Carvalho Chehab * Index of a string descriptor that describes this 8360c0d06caSMauro Carvalho Chehab * extension unit. 8370c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 8380c0d06caSMauro Carvalho Chehab */ 8390c0d06caSMauro Carvalho Chehab p = buflen >= 22 ? buffer[21] : 0; 8400c0d06caSMauro Carvalho Chehab n = buflen >= 25 + p ? buffer[22+p] : 0; 8410c0d06caSMauro Carvalho Chehab 8420c0d06caSMauro Carvalho Chehab if (buflen < 25 + p + 2*n) { 8439e56380aSJoe Perches uvc_dbg(dev, DESCR, 844ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d EXTENSION_UNIT error\n", 8450c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 8460c0d06caSMauro Carvalho Chehab break; 8470c0d06caSMauro Carvalho Chehab } 8480c0d06caSMauro Carvalho Chehab 8490c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3], 8500c0d06caSMauro Carvalho Chehab p + 1, 2*n); 8510c0d06caSMauro Carvalho Chehab if (unit == NULL) 8520c0d06caSMauro Carvalho Chehab return -ENOMEM; 8530c0d06caSMauro Carvalho Chehab 854351509c6SRicardo Ribalda memcpy(unit->guid, &buffer[4], 16); 8550c0d06caSMauro Carvalho Chehab unit->extension.bNumControls = buffer[20]; 8560c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[22], p); 8570c0d06caSMauro Carvalho Chehab unit->extension.bControlSize = buffer[22+p]; 8582c6b222cSLaurent Pinchart unit->extension.bmControls = (u8 *)unit + sizeof(*unit); 8592c6b222cSLaurent Pinchart unit->extension.bmControlsType = (u8 *)unit + sizeof(*unit) 8600c0d06caSMauro Carvalho Chehab + n; 8610c0d06caSMauro Carvalho Chehab memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); 8620c0d06caSMauro Carvalho Chehab 8634867bb59SGuenter Roeck if (buffer[24+p+2*n] == 0 || 8644867bb59SGuenter Roeck usb_string(udev, buffer[24+p+2*n], unit->name, sizeof(unit->name)) < 0) 8650c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Extension %u", buffer[3]); 8660c0d06caSMauro Carvalho Chehab 8670c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 8680c0d06caSMauro Carvalho Chehab handled = 1; 8690c0d06caSMauro Carvalho Chehab break; 8700c0d06caSMauro Carvalho Chehab } 8710c0d06caSMauro Carvalho Chehab 8720c0d06caSMauro Carvalho Chehab return handled; 8730c0d06caSMauro Carvalho Chehab } 8740c0d06caSMauro Carvalho Chehab 8750c0d06caSMauro Carvalho Chehab static int uvc_parse_standard_control(struct uvc_device *dev, 8760c0d06caSMauro Carvalho Chehab const unsigned char *buffer, int buflen) 8770c0d06caSMauro Carvalho Chehab { 8780c0d06caSMauro Carvalho Chehab struct usb_device *udev = dev->udev; 8790c0d06caSMauro Carvalho Chehab struct uvc_entity *unit, *term; 8800c0d06caSMauro Carvalho Chehab struct usb_interface *intf; 8810c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 8820c0d06caSMauro Carvalho Chehab unsigned int i, n, p, len; 8832c6b222cSLaurent Pinchart u16 type; 8840c0d06caSMauro Carvalho Chehab 8850c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 8860c0d06caSMauro Carvalho Chehab case UVC_VC_HEADER: 8870c0d06caSMauro Carvalho Chehab n = buflen >= 12 ? buffer[11] : 0; 8880c0d06caSMauro Carvalho Chehab 889daf41ac2SOliver Neukum if (buflen < 12 + n) { 8909e56380aSJoe Perches uvc_dbg(dev, DESCR, 891ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d HEADER error\n", 892ed4c5fa4SRicardo Ribalda udev->devnum, alts->desc.bInterfaceNumber); 8930c0d06caSMauro Carvalho Chehab return -EINVAL; 8940c0d06caSMauro Carvalho Chehab } 8950c0d06caSMauro Carvalho Chehab 8960c0d06caSMauro Carvalho Chehab dev->uvc_version = get_unaligned_le16(&buffer[3]); 8970c0d06caSMauro Carvalho Chehab dev->clock_frequency = get_unaligned_le32(&buffer[7]); 8980c0d06caSMauro Carvalho Chehab 8990c0d06caSMauro Carvalho Chehab /* Parse all USB Video Streaming interfaces. */ 9000c0d06caSMauro Carvalho Chehab for (i = 0; i < n; ++i) { 9010c0d06caSMauro Carvalho Chehab intf = usb_ifnum_to_if(udev, buffer[12+i]); 9020c0d06caSMauro Carvalho Chehab if (intf == NULL) { 9039e56380aSJoe Perches uvc_dbg(dev, DESCR, 904ed4c5fa4SRicardo Ribalda "device %d interface %d doesn't exists\n", 9050c0d06caSMauro Carvalho Chehab udev->devnum, i); 9060c0d06caSMauro Carvalho Chehab continue; 9070c0d06caSMauro Carvalho Chehab } 9080c0d06caSMauro Carvalho Chehab 9090c0d06caSMauro Carvalho Chehab uvc_parse_streaming(dev, intf); 9100c0d06caSMauro Carvalho Chehab } 9110c0d06caSMauro Carvalho Chehab break; 9120c0d06caSMauro Carvalho Chehab 9130c0d06caSMauro Carvalho Chehab case UVC_VC_INPUT_TERMINAL: 9140c0d06caSMauro Carvalho Chehab if (buflen < 8) { 9159e56380aSJoe Perches uvc_dbg(dev, DESCR, 916ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d INPUT_TERMINAL error\n", 9170c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 9180c0d06caSMauro Carvalho Chehab return -EINVAL; 9190c0d06caSMauro Carvalho Chehab } 9200c0d06caSMauro Carvalho Chehab 92147bb1179SAlistair Strachan /* 92247bb1179SAlistair Strachan * Reject invalid terminal types that would cause issues: 92347bb1179SAlistair Strachan * 92447bb1179SAlistair Strachan * - The high byte must be non-zero, otherwise it would be 92547bb1179SAlistair Strachan * confused with a unit. 92647bb1179SAlistair Strachan * 92747bb1179SAlistair Strachan * - Bit 15 must be 0, as we use it internally as a terminal 92847bb1179SAlistair Strachan * direction flag. 92947bb1179SAlistair Strachan * 93047bb1179SAlistair Strachan * Other unknown types are accepted. 9310c0d06caSMauro Carvalho Chehab */ 9320c0d06caSMauro Carvalho Chehab type = get_unaligned_le16(&buffer[4]); 93347bb1179SAlistair Strachan if ((type & 0x7f00) == 0 || (type & 0x8000) != 0) { 9349e56380aSJoe Perches uvc_dbg(dev, DESCR, 935ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d INPUT_TERMINAL %d has invalid type 0x%04x, skipping\n", 936ed4c5fa4SRicardo Ribalda udev->devnum, alts->desc.bInterfaceNumber, 9370c0d06caSMauro Carvalho Chehab buffer[3], type); 9380c0d06caSMauro Carvalho Chehab return 0; 9390c0d06caSMauro Carvalho Chehab } 9400c0d06caSMauro Carvalho Chehab 9410c0d06caSMauro Carvalho Chehab n = 0; 9420c0d06caSMauro Carvalho Chehab p = 0; 9430c0d06caSMauro Carvalho Chehab len = 8; 9440c0d06caSMauro Carvalho Chehab 9450c0d06caSMauro Carvalho Chehab if (type == UVC_ITT_CAMERA) { 9460c0d06caSMauro Carvalho Chehab n = buflen >= 15 ? buffer[14] : 0; 9470c0d06caSMauro Carvalho Chehab len = 15; 9480c0d06caSMauro Carvalho Chehab 9490c0d06caSMauro Carvalho Chehab } else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) { 9500c0d06caSMauro Carvalho Chehab n = buflen >= 9 ? buffer[8] : 0; 9510c0d06caSMauro Carvalho Chehab p = buflen >= 10 + n ? buffer[9+n] : 0; 9520c0d06caSMauro Carvalho Chehab len = 10; 9530c0d06caSMauro Carvalho Chehab } 9540c0d06caSMauro Carvalho Chehab 9550c0d06caSMauro Carvalho Chehab if (buflen < len + n + p) { 9569e56380aSJoe Perches uvc_dbg(dev, DESCR, 957ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d INPUT_TERMINAL error\n", 9580c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 9590c0d06caSMauro Carvalho Chehab return -EINVAL; 9600c0d06caSMauro Carvalho Chehab } 9610c0d06caSMauro Carvalho Chehab 9620c0d06caSMauro Carvalho Chehab term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3], 9630c0d06caSMauro Carvalho Chehab 1, n + p); 9640c0d06caSMauro Carvalho Chehab if (term == NULL) 9650c0d06caSMauro Carvalho Chehab return -ENOMEM; 9660c0d06caSMauro Carvalho Chehab 9670c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) { 9680c0d06caSMauro Carvalho Chehab term->camera.bControlSize = n; 969f14d4988SLaurent Pinchart term->camera.bmControls = (u8 *)term + sizeof(*term); 9700c0d06caSMauro Carvalho Chehab term->camera.wObjectiveFocalLengthMin = 9710c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[8]); 9720c0d06caSMauro Carvalho Chehab term->camera.wObjectiveFocalLengthMax = 9730c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[10]); 9740c0d06caSMauro Carvalho Chehab term->camera.wOcularFocalLength = 9750c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[12]); 9760c0d06caSMauro Carvalho Chehab memcpy(term->camera.bmControls, &buffer[15], n); 9770c0d06caSMauro Carvalho Chehab } else if (UVC_ENTITY_TYPE(term) == 9780c0d06caSMauro Carvalho Chehab UVC_ITT_MEDIA_TRANSPORT_INPUT) { 9790c0d06caSMauro Carvalho Chehab term->media.bControlSize = n; 980f14d4988SLaurent Pinchart term->media.bmControls = (u8 *)term + sizeof(*term); 9810c0d06caSMauro Carvalho Chehab term->media.bTransportModeSize = p; 9822c6b222cSLaurent Pinchart term->media.bmTransportModes = (u8 *)term 983f14d4988SLaurent Pinchart + sizeof(*term) + n; 9840c0d06caSMauro Carvalho Chehab memcpy(term->media.bmControls, &buffer[9], n); 9850c0d06caSMauro Carvalho Chehab memcpy(term->media.bmTransportModes, &buffer[10+n], p); 9860c0d06caSMauro Carvalho Chehab } 9870c0d06caSMauro Carvalho Chehab 9884867bb59SGuenter Roeck if (buffer[7] == 0 || 9894867bb59SGuenter Roeck usb_string(udev, buffer[7], term->name, sizeof(term->name)) < 0) { 9904867bb59SGuenter Roeck if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) 9910c0d06caSMauro Carvalho Chehab sprintf(term->name, "Camera %u", buffer[3]); 9924867bb59SGuenter Roeck if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT) 9930c0d06caSMauro Carvalho Chehab sprintf(term->name, "Media %u", buffer[3]); 9940c0d06caSMauro Carvalho Chehab else 9950c0d06caSMauro Carvalho Chehab sprintf(term->name, "Input %u", buffer[3]); 9964867bb59SGuenter Roeck } 9970c0d06caSMauro Carvalho Chehab 9980c0d06caSMauro Carvalho Chehab list_add_tail(&term->list, &dev->entities); 9990c0d06caSMauro Carvalho Chehab break; 10000c0d06caSMauro Carvalho Chehab 10010c0d06caSMauro Carvalho Chehab case UVC_VC_OUTPUT_TERMINAL: 10020c0d06caSMauro Carvalho Chehab if (buflen < 9) { 10039e56380aSJoe Perches uvc_dbg(dev, DESCR, 1004ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d OUTPUT_TERMINAL error\n", 10050c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 10060c0d06caSMauro Carvalho Chehab return -EINVAL; 10070c0d06caSMauro Carvalho Chehab } 10080c0d06caSMauro Carvalho Chehab 1009699b9a86SLaurent Pinchart /* 1010699b9a86SLaurent Pinchart * Make sure the terminal type MSB is not null, otherwise it 10110c0d06caSMauro Carvalho Chehab * could be confused with a unit. 10120c0d06caSMauro Carvalho Chehab */ 10130c0d06caSMauro Carvalho Chehab type = get_unaligned_le16(&buffer[4]); 10140c0d06caSMauro Carvalho Chehab if ((type & 0xff00) == 0) { 10159e56380aSJoe Perches uvc_dbg(dev, DESCR, 1016ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d OUTPUT_TERMINAL %d has invalid type 0x%04x, skipping\n", 1017ed4c5fa4SRicardo Ribalda udev->devnum, alts->desc.bInterfaceNumber, 1018ed4c5fa4SRicardo Ribalda buffer[3], type); 10190c0d06caSMauro Carvalho Chehab return 0; 10200c0d06caSMauro Carvalho Chehab } 10210c0d06caSMauro Carvalho Chehab 10220c0d06caSMauro Carvalho Chehab term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3], 10230c0d06caSMauro Carvalho Chehab 1, 0); 10240c0d06caSMauro Carvalho Chehab if (term == NULL) 10250c0d06caSMauro Carvalho Chehab return -ENOMEM; 10260c0d06caSMauro Carvalho Chehab 10270c0d06caSMauro Carvalho Chehab memcpy(term->baSourceID, &buffer[7], 1); 10280c0d06caSMauro Carvalho Chehab 10294867bb59SGuenter Roeck if (buffer[8] == 0 || 10304867bb59SGuenter Roeck usb_string(udev, buffer[8], term->name, sizeof(term->name)) < 0) 10310c0d06caSMauro Carvalho Chehab sprintf(term->name, "Output %u", buffer[3]); 10320c0d06caSMauro Carvalho Chehab 10330c0d06caSMauro Carvalho Chehab list_add_tail(&term->list, &dev->entities); 10340c0d06caSMauro Carvalho Chehab break; 10350c0d06caSMauro Carvalho Chehab 10360c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 10370c0d06caSMauro Carvalho Chehab p = buflen >= 5 ? buffer[4] : 0; 10380c0d06caSMauro Carvalho Chehab 10390c0d06caSMauro Carvalho Chehab if (buflen < 5 || buflen < 6 + p) { 10409e56380aSJoe Perches uvc_dbg(dev, DESCR, 1041ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d SELECTOR_UNIT error\n", 10420c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 10430c0d06caSMauro Carvalho Chehab return -EINVAL; 10440c0d06caSMauro Carvalho Chehab } 10450c0d06caSMauro Carvalho Chehab 10460c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0); 10470c0d06caSMauro Carvalho Chehab if (unit == NULL) 10480c0d06caSMauro Carvalho Chehab return -ENOMEM; 10490c0d06caSMauro Carvalho Chehab 10500c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[5], p); 10510c0d06caSMauro Carvalho Chehab 10524867bb59SGuenter Roeck if (buffer[5+p] == 0 || 10534867bb59SGuenter Roeck usb_string(udev, buffer[5+p], unit->name, sizeof(unit->name)) < 0) 10540c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Selector %u", buffer[3]); 10550c0d06caSMauro Carvalho Chehab 10560c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 10570c0d06caSMauro Carvalho Chehab break; 10580c0d06caSMauro Carvalho Chehab 10590c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 10600c0d06caSMauro Carvalho Chehab n = buflen >= 8 ? buffer[7] : 0; 10610c0d06caSMauro Carvalho Chehab p = dev->uvc_version >= 0x0110 ? 10 : 9; 10620c0d06caSMauro Carvalho Chehab 10630c0d06caSMauro Carvalho Chehab if (buflen < p + n) { 10649e56380aSJoe Perches uvc_dbg(dev, DESCR, 1065ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d PROCESSING_UNIT error\n", 10660c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 10670c0d06caSMauro Carvalho Chehab return -EINVAL; 10680c0d06caSMauro Carvalho Chehab } 10690c0d06caSMauro Carvalho Chehab 10700c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n); 10710c0d06caSMauro Carvalho Chehab if (unit == NULL) 10720c0d06caSMauro Carvalho Chehab return -ENOMEM; 10730c0d06caSMauro Carvalho Chehab 10740c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[4], 1); 10750c0d06caSMauro Carvalho Chehab unit->processing.wMaxMultiplier = 10760c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[5]); 10770c0d06caSMauro Carvalho Chehab unit->processing.bControlSize = buffer[7]; 1078f14d4988SLaurent Pinchart unit->processing.bmControls = (u8 *)unit + sizeof(*unit); 10790c0d06caSMauro Carvalho Chehab memcpy(unit->processing.bmControls, &buffer[8], n); 10800c0d06caSMauro Carvalho Chehab if (dev->uvc_version >= 0x0110) 10810c0d06caSMauro Carvalho Chehab unit->processing.bmVideoStandards = buffer[9+n]; 10820c0d06caSMauro Carvalho Chehab 10834867bb59SGuenter Roeck if (buffer[8+n] == 0 || 10844867bb59SGuenter Roeck usb_string(udev, buffer[8+n], unit->name, sizeof(unit->name)) < 0) 10850c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Processing %u", buffer[3]); 10860c0d06caSMauro Carvalho Chehab 10870c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 10880c0d06caSMauro Carvalho Chehab break; 10890c0d06caSMauro Carvalho Chehab 10900c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 10910c0d06caSMauro Carvalho Chehab p = buflen >= 22 ? buffer[21] : 0; 10920c0d06caSMauro Carvalho Chehab n = buflen >= 24 + p ? buffer[22+p] : 0; 10930c0d06caSMauro Carvalho Chehab 10940c0d06caSMauro Carvalho Chehab if (buflen < 24 + p + n) { 10959e56380aSJoe Perches uvc_dbg(dev, DESCR, 1096ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d EXTENSION_UNIT error\n", 10970c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 10980c0d06caSMauro Carvalho Chehab return -EINVAL; 10990c0d06caSMauro Carvalho Chehab } 11000c0d06caSMauro Carvalho Chehab 11010c0d06caSMauro Carvalho Chehab unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n); 11020c0d06caSMauro Carvalho Chehab if (unit == NULL) 11030c0d06caSMauro Carvalho Chehab return -ENOMEM; 11040c0d06caSMauro Carvalho Chehab 1105351509c6SRicardo Ribalda memcpy(unit->guid, &buffer[4], 16); 11060c0d06caSMauro Carvalho Chehab unit->extension.bNumControls = buffer[20]; 11070c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[22], p); 11080c0d06caSMauro Carvalho Chehab unit->extension.bControlSize = buffer[22+p]; 1109f14d4988SLaurent Pinchart unit->extension.bmControls = (u8 *)unit + sizeof(*unit); 11100c0d06caSMauro Carvalho Chehab memcpy(unit->extension.bmControls, &buffer[23+p], n); 11110c0d06caSMauro Carvalho Chehab 11124867bb59SGuenter Roeck if (buffer[23+p+n] == 0 || 11134867bb59SGuenter Roeck usb_string(udev, buffer[23+p+n], unit->name, sizeof(unit->name)) < 0) 11140c0d06caSMauro Carvalho Chehab sprintf(unit->name, "Extension %u", buffer[3]); 11150c0d06caSMauro Carvalho Chehab 11160c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 11170c0d06caSMauro Carvalho Chehab break; 11180c0d06caSMauro Carvalho Chehab 11190c0d06caSMauro Carvalho Chehab default: 11209e56380aSJoe Perches uvc_dbg(dev, DESCR, 1121ed4c5fa4SRicardo Ribalda "Found an unknown CS_INTERFACE descriptor (%u)\n", 1122ed4c5fa4SRicardo Ribalda buffer[2]); 11230c0d06caSMauro Carvalho Chehab break; 11240c0d06caSMauro Carvalho Chehab } 11250c0d06caSMauro Carvalho Chehab 11260c0d06caSMauro Carvalho Chehab return 0; 11270c0d06caSMauro Carvalho Chehab } 11280c0d06caSMauro Carvalho Chehab 11290c0d06caSMauro Carvalho Chehab static int uvc_parse_control(struct uvc_device *dev) 11300c0d06caSMauro Carvalho Chehab { 11310c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 11320c0d06caSMauro Carvalho Chehab unsigned char *buffer = alts->extra; 11330c0d06caSMauro Carvalho Chehab int buflen = alts->extralen; 11340c0d06caSMauro Carvalho Chehab int ret; 11350c0d06caSMauro Carvalho Chehab 1136699b9a86SLaurent Pinchart /* 1137699b9a86SLaurent Pinchart * Parse the default alternate setting only, as the UVC specification 11380c0d06caSMauro Carvalho Chehab * defines a single alternate setting, the default alternate setting 11390c0d06caSMauro Carvalho Chehab * zero. 11400c0d06caSMauro Carvalho Chehab */ 11410c0d06caSMauro Carvalho Chehab 11420c0d06caSMauro Carvalho Chehab while (buflen > 2) { 11430c0d06caSMauro Carvalho Chehab if (uvc_parse_vendor_control(dev, buffer, buflen) || 11440c0d06caSMauro Carvalho Chehab buffer[1] != USB_DT_CS_INTERFACE) 11450c0d06caSMauro Carvalho Chehab goto next_descriptor; 11460c0d06caSMauro Carvalho Chehab 11477b78a846SPedro Guilherme Siqueira Moreira ret = uvc_parse_standard_control(dev, buffer, buflen); 11487b78a846SPedro Guilherme Siqueira Moreira if (ret < 0) 11490c0d06caSMauro Carvalho Chehab return ret; 11500c0d06caSMauro Carvalho Chehab 11510c0d06caSMauro Carvalho Chehab next_descriptor: 11520c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 11530c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 11540c0d06caSMauro Carvalho Chehab } 11550c0d06caSMauro Carvalho Chehab 1156699b9a86SLaurent Pinchart /* 1157699b9a86SLaurent Pinchart * Check if the optional status endpoint is present. Built-in iSight 11580c0d06caSMauro Carvalho Chehab * webcams have an interrupt endpoint but spit proprietary data that 11590c0d06caSMauro Carvalho Chehab * don't conform to the UVC status endpoint messages. Don't try to 11600c0d06caSMauro Carvalho Chehab * handle the interrupt endpoint for those cameras. 11610c0d06caSMauro Carvalho Chehab */ 11620c0d06caSMauro Carvalho Chehab if (alts->desc.bNumEndpoints == 1 && 11630c0d06caSMauro Carvalho Chehab !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) { 11640c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep = &alts->endpoint[0]; 11650c0d06caSMauro Carvalho Chehab struct usb_endpoint_descriptor *desc = &ep->desc; 11660c0d06caSMauro Carvalho Chehab 11670c0d06caSMauro Carvalho Chehab if (usb_endpoint_is_int_in(desc) && 11680c0d06caSMauro Carvalho Chehab le16_to_cpu(desc->wMaxPacketSize) >= 8 && 11690c0d06caSMauro Carvalho Chehab desc->bInterval != 0) { 11709e56380aSJoe Perches uvc_dbg(dev, DESCR, 11719e56380aSJoe Perches "Found a Status endpoint (addr %02x)\n", 1172ed4c5fa4SRicardo Ribalda desc->bEndpointAddress); 11730c0d06caSMauro Carvalho Chehab dev->int_ep = ep; 11740c0d06caSMauro Carvalho Chehab } 11750c0d06caSMauro Carvalho Chehab } 11760c0d06caSMauro Carvalho Chehab 11770c0d06caSMauro Carvalho Chehab return 0; 11780c0d06caSMauro Carvalho Chehab } 11790c0d06caSMauro Carvalho Chehab 11802886477fSRicardo Ribalda /* ----------------------------------------------------------------------------- 11812886477fSRicardo Ribalda * Privacy GPIO 11822886477fSRicardo Ribalda */ 11832886477fSRicardo Ribalda 11842886477fSRicardo Ribalda static void uvc_gpio_event(struct uvc_device *dev) 11852886477fSRicardo Ribalda { 11862886477fSRicardo Ribalda struct uvc_entity *unit = dev->gpio_unit; 11872886477fSRicardo Ribalda struct uvc_video_chain *chain; 11882886477fSRicardo Ribalda u8 new_val; 11892886477fSRicardo Ribalda 11902886477fSRicardo Ribalda if (!unit) 11912886477fSRicardo Ribalda return; 11922886477fSRicardo Ribalda 11932886477fSRicardo Ribalda new_val = gpiod_get_value_cansleep(unit->gpio.gpio_privacy); 11942886477fSRicardo Ribalda 11952886477fSRicardo Ribalda /* GPIO entities are always on the first chain. */ 11962886477fSRicardo Ribalda chain = list_first_entry(&dev->chains, struct uvc_video_chain, list); 11972886477fSRicardo Ribalda uvc_ctrl_status_event(chain, unit->controls, &new_val); 11982886477fSRicardo Ribalda } 11992886477fSRicardo Ribalda 12002886477fSRicardo Ribalda static int uvc_gpio_get_cur(struct uvc_device *dev, struct uvc_entity *entity, 12012886477fSRicardo Ribalda u8 cs, void *data, u16 size) 12022886477fSRicardo Ribalda { 12032886477fSRicardo Ribalda if (cs != UVC_CT_PRIVACY_CONTROL || size < 1) 12042886477fSRicardo Ribalda return -EINVAL; 12052886477fSRicardo Ribalda 12062886477fSRicardo Ribalda *(u8 *)data = gpiod_get_value_cansleep(entity->gpio.gpio_privacy); 12072886477fSRicardo Ribalda 12082886477fSRicardo Ribalda return 0; 12092886477fSRicardo Ribalda } 12102886477fSRicardo Ribalda 12112886477fSRicardo Ribalda static int uvc_gpio_get_info(struct uvc_device *dev, struct uvc_entity *entity, 12122886477fSRicardo Ribalda u8 cs, u8 *caps) 12132886477fSRicardo Ribalda { 12142886477fSRicardo Ribalda if (cs != UVC_CT_PRIVACY_CONTROL) 12152886477fSRicardo Ribalda return -EINVAL; 12162886477fSRicardo Ribalda 12172886477fSRicardo Ribalda *caps = UVC_CONTROL_CAP_GET | UVC_CONTROL_CAP_AUTOUPDATE; 12182886477fSRicardo Ribalda return 0; 12192886477fSRicardo Ribalda } 12202886477fSRicardo Ribalda 12212886477fSRicardo Ribalda static irqreturn_t uvc_gpio_irq(int irq, void *data) 12222886477fSRicardo Ribalda { 12232886477fSRicardo Ribalda struct uvc_device *dev = data; 12242886477fSRicardo Ribalda 12252886477fSRicardo Ribalda uvc_gpio_event(dev); 12262886477fSRicardo Ribalda return IRQ_HANDLED; 12272886477fSRicardo Ribalda } 12282886477fSRicardo Ribalda 12292886477fSRicardo Ribalda static int uvc_gpio_parse(struct uvc_device *dev) 12302886477fSRicardo Ribalda { 12312886477fSRicardo Ribalda struct uvc_entity *unit; 12322886477fSRicardo Ribalda struct gpio_desc *gpio_privacy; 12332886477fSRicardo Ribalda int irq; 12342886477fSRicardo Ribalda 12352886477fSRicardo Ribalda gpio_privacy = devm_gpiod_get_optional(&dev->udev->dev, "privacy", 12362886477fSRicardo Ribalda GPIOD_IN); 12372886477fSRicardo Ribalda if (IS_ERR_OR_NULL(gpio_privacy)) 12382886477fSRicardo Ribalda return PTR_ERR_OR_ZERO(gpio_privacy); 12392886477fSRicardo Ribalda 12402886477fSRicardo Ribalda irq = gpiod_to_irq(gpio_privacy); 12416cb7d1b3SYang Yingliang if (irq < 0) 12426cb7d1b3SYang Yingliang return dev_err_probe(&dev->udev->dev, irq, 12436cb7d1b3SYang Yingliang "No IRQ for privacy GPIO\n"); 12442886477fSRicardo Ribalda 1245f0f07845SJosé Expósito unit = uvc_alloc_entity(UVC_EXT_GPIO_UNIT, UVC_EXT_GPIO_UNIT_ID, 0, 1); 1246f0f07845SJosé Expósito if (!unit) 1247f0f07845SJosé Expósito return -ENOMEM; 1248f0f07845SJosé Expósito 12492886477fSRicardo Ribalda unit->gpio.gpio_privacy = gpio_privacy; 12502886477fSRicardo Ribalda unit->gpio.irq = irq; 12512886477fSRicardo Ribalda unit->gpio.bControlSize = 1; 12522886477fSRicardo Ribalda unit->gpio.bmControls = (u8 *)unit + sizeof(*unit); 12532886477fSRicardo Ribalda unit->gpio.bmControls[0] = 1; 12542886477fSRicardo Ribalda unit->get_cur = uvc_gpio_get_cur; 12552886477fSRicardo Ribalda unit->get_info = uvc_gpio_get_info; 1256063b811fSHans Verkuil strscpy(unit->name, "GPIO", sizeof(unit->name)); 12572886477fSRicardo Ribalda 12582886477fSRicardo Ribalda list_add_tail(&unit->list, &dev->entities); 12592886477fSRicardo Ribalda 12602886477fSRicardo Ribalda dev->gpio_unit = unit; 12612886477fSRicardo Ribalda 12622886477fSRicardo Ribalda return 0; 12632886477fSRicardo Ribalda } 12642886477fSRicardo Ribalda 12652886477fSRicardo Ribalda static int uvc_gpio_init_irq(struct uvc_device *dev) 12662886477fSRicardo Ribalda { 12672886477fSRicardo Ribalda struct uvc_entity *unit = dev->gpio_unit; 12682886477fSRicardo Ribalda 12692886477fSRicardo Ribalda if (!unit || unit->gpio.irq < 0) 12702886477fSRicardo Ribalda return 0; 12712886477fSRicardo Ribalda 12722886477fSRicardo Ribalda return devm_request_threaded_irq(&dev->udev->dev, unit->gpio.irq, NULL, 12732886477fSRicardo Ribalda uvc_gpio_irq, 12742886477fSRicardo Ribalda IRQF_ONESHOT | IRQF_TRIGGER_FALLING | 12752886477fSRicardo Ribalda IRQF_TRIGGER_RISING, 12762886477fSRicardo Ribalda "uvc_privacy_gpio", dev); 12772886477fSRicardo Ribalda } 12782886477fSRicardo Ribalda 12790c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 12800c0d06caSMauro Carvalho Chehab * UVC device scan 12810c0d06caSMauro Carvalho Chehab */ 12820c0d06caSMauro Carvalho Chehab 12830c0d06caSMauro Carvalho Chehab /* 12840c0d06caSMauro Carvalho Chehab * Scan the UVC descriptors to locate a chain starting at an Output Terminal 12850c0d06caSMauro Carvalho Chehab * and containing the following units: 12860c0d06caSMauro Carvalho Chehab * 12870c0d06caSMauro Carvalho Chehab * - one or more Output Terminals (USB Streaming or Display) 12880c0d06caSMauro Carvalho Chehab * - zero or one Processing Unit 12890c0d06caSMauro Carvalho Chehab * - zero, one or more single-input Selector Units 12900c0d06caSMauro Carvalho Chehab * - zero or one multiple-input Selector Units, provided all inputs are 12910c0d06caSMauro Carvalho Chehab * connected to input terminals 12920c0d06caSMauro Carvalho Chehab * - zero, one or mode single-input Extension Units 12930c0d06caSMauro Carvalho Chehab * - one or more Input Terminals (Camera, External or USB Streaming) 12940c0d06caSMauro Carvalho Chehab * 12950c0d06caSMauro Carvalho Chehab * The terminal and units must match on of the following structures: 12960c0d06caSMauro Carvalho Chehab * 12970c0d06caSMauro Carvalho Chehab * ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0) 12980c0d06caSMauro Carvalho Chehab * ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ... 12990c0d06caSMauro Carvalho Chehab * ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n) 13000c0d06caSMauro Carvalho Chehab * 13010c0d06caSMauro Carvalho Chehab * +---------+ +---------+ -> OTT_*(0) 13020c0d06caSMauro Carvalho Chehab * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ... 13030c0d06caSMauro Carvalho Chehab * +---------+ +---------+ -> OTT_*(n) 13040c0d06caSMauro Carvalho Chehab * 13050c0d06caSMauro Carvalho Chehab * The Processing Unit and Extension Units can be in any order. Additional 13060c0d06caSMauro Carvalho Chehab * Extension Units connected to the main chain as single-unit branches are 13070c0d06caSMauro Carvalho Chehab * also supported. Single-input Selector Units are ignored. 13080c0d06caSMauro Carvalho Chehab */ 13090c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_entity(struct uvc_video_chain *chain, 13100c0d06caSMauro Carvalho Chehab struct uvc_entity *entity) 13110c0d06caSMauro Carvalho Chehab { 13120c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(entity)) { 13130c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 13149e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- XU %d", entity->id); 13150c0d06caSMauro Carvalho Chehab 13160c0d06caSMauro Carvalho Chehab if (entity->bNrInPins != 1) { 13179e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 13189e56380aSJoe Perches "Extension unit %d has more than 1 input pin\n", 1319ed4c5fa4SRicardo Ribalda entity->id); 13200c0d06caSMauro Carvalho Chehab return -1; 13210c0d06caSMauro Carvalho Chehab } 13220c0d06caSMauro Carvalho Chehab 13230c0d06caSMauro Carvalho Chehab break; 13240c0d06caSMauro Carvalho Chehab 13250c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 13269e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- PU %d", entity->id); 13270c0d06caSMauro Carvalho Chehab 13280c0d06caSMauro Carvalho Chehab if (chain->processing != NULL) { 13299e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 13309e56380aSJoe Perches "Found multiple Processing Units in chain\n"); 13310c0d06caSMauro Carvalho Chehab return -1; 13320c0d06caSMauro Carvalho Chehab } 13330c0d06caSMauro Carvalho Chehab 13340c0d06caSMauro Carvalho Chehab chain->processing = entity; 13350c0d06caSMauro Carvalho Chehab break; 13360c0d06caSMauro Carvalho Chehab 13370c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 13389e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- SU %d", entity->id); 13390c0d06caSMauro Carvalho Chehab 13400c0d06caSMauro Carvalho Chehab /* Single-input selector units are ignored. */ 13410c0d06caSMauro Carvalho Chehab if (entity->bNrInPins == 1) 13420c0d06caSMauro Carvalho Chehab break; 13430c0d06caSMauro Carvalho Chehab 13440c0d06caSMauro Carvalho Chehab if (chain->selector != NULL) { 13459e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 13469e56380aSJoe Perches "Found multiple Selector Units in chain\n"); 13470c0d06caSMauro Carvalho Chehab return -1; 13480c0d06caSMauro Carvalho Chehab } 13490c0d06caSMauro Carvalho Chehab 13500c0d06caSMauro Carvalho Chehab chain->selector = entity; 13510c0d06caSMauro Carvalho Chehab break; 13520c0d06caSMauro Carvalho Chehab 13530c0d06caSMauro Carvalho Chehab case UVC_ITT_VENDOR_SPECIFIC: 13540c0d06caSMauro Carvalho Chehab case UVC_ITT_CAMERA: 13550c0d06caSMauro Carvalho Chehab case UVC_ITT_MEDIA_TRANSPORT_INPUT: 13569e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- IT %d\n", entity->id); 13570c0d06caSMauro Carvalho Chehab 13580c0d06caSMauro Carvalho Chehab break; 13590c0d06caSMauro Carvalho Chehab 13600c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 13610c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 13620c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 13639e56380aSJoe Perches uvc_dbg_cont(PROBE, " OT %d", entity->id); 13640c0d06caSMauro Carvalho Chehab 13650c0d06caSMauro Carvalho Chehab break; 13660c0d06caSMauro Carvalho Chehab 13670c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 136859e92bf6SRicardo Ribalda if (UVC_ENTITY_IS_ITERM(entity)) 13699e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- IT %d\n", entity->id); 137059e92bf6SRicardo Ribalda else 13719e56380aSJoe Perches uvc_dbg_cont(PROBE, " OT %d", entity->id); 13720c0d06caSMauro Carvalho Chehab 13730c0d06caSMauro Carvalho Chehab break; 13740c0d06caSMauro Carvalho Chehab 13750c0d06caSMauro Carvalho Chehab default: 13769e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 13779e56380aSJoe Perches "Unsupported entity type 0x%04x found in chain\n", 1378ed4c5fa4SRicardo Ribalda UVC_ENTITY_TYPE(entity)); 13790c0d06caSMauro Carvalho Chehab return -1; 13800c0d06caSMauro Carvalho Chehab } 13810c0d06caSMauro Carvalho Chehab 13820c0d06caSMauro Carvalho Chehab list_add_tail(&entity->chain, &chain->entities); 13830c0d06caSMauro Carvalho Chehab return 0; 13840c0d06caSMauro Carvalho Chehab } 13850c0d06caSMauro Carvalho Chehab 13860c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_forward(struct uvc_video_chain *chain, 13870c0d06caSMauro Carvalho Chehab struct uvc_entity *entity, struct uvc_entity *prev) 13880c0d06caSMauro Carvalho Chehab { 13890c0d06caSMauro Carvalho Chehab struct uvc_entity *forward; 13900c0d06caSMauro Carvalho Chehab int found; 13910c0d06caSMauro Carvalho Chehab 13920c0d06caSMauro Carvalho Chehab /* Forward scan */ 13930c0d06caSMauro Carvalho Chehab forward = NULL; 13940c0d06caSMauro Carvalho Chehab found = 0; 13950c0d06caSMauro Carvalho Chehab 13960c0d06caSMauro Carvalho Chehab while (1) { 13970c0d06caSMauro Carvalho Chehab forward = uvc_entity_by_reference(chain->dev, entity->id, 13980c0d06caSMauro Carvalho Chehab forward); 13990c0d06caSMauro Carvalho Chehab if (forward == NULL) 14000c0d06caSMauro Carvalho Chehab break; 14010c0d06caSMauro Carvalho Chehab if (forward == prev) 14020c0d06caSMauro Carvalho Chehab continue; 140368035c80SWill Deacon if (forward->chain.next || forward->chain.prev) { 14049e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 14059e56380aSJoe Perches "Found reference to entity %d already in chain\n", 1406ed4c5fa4SRicardo Ribalda forward->id); 140768035c80SWill Deacon return -EINVAL; 140868035c80SWill Deacon } 14090c0d06caSMauro Carvalho Chehab 14100c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(forward)) { 14110c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 14120c0d06caSMauro Carvalho Chehab if (forward->bNrInPins != 1) { 14139e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 14149e56380aSJoe Perches "Extension unit %d has more than 1 input pin\n", 141532934486SLaurent Pinchart forward->id); 14160c0d06caSMauro Carvalho Chehab return -EINVAL; 14170c0d06caSMauro Carvalho Chehab } 14180c0d06caSMauro Carvalho Chehab 14194ca052b4SLaurent Pinchart /* 14204ca052b4SLaurent Pinchart * Some devices reference an output terminal as the 14214ca052b4SLaurent Pinchart * source of extension units. This is incorrect, as 14224ca052b4SLaurent Pinchart * output terminals only have an input pin, and thus 14234ca052b4SLaurent Pinchart * can't be connected to any entity in the forward 14244ca052b4SLaurent Pinchart * direction. The resulting topology would cause issues 14254ca052b4SLaurent Pinchart * when registering the media controller graph. To 14264ca052b4SLaurent Pinchart * avoid this problem, connect the extension unit to 14274ca052b4SLaurent Pinchart * the source of the output terminal instead. 14284ca052b4SLaurent Pinchart */ 14294ca052b4SLaurent Pinchart if (UVC_ENTITY_IS_OTERM(entity)) { 14304ca052b4SLaurent Pinchart struct uvc_entity *source; 14314ca052b4SLaurent Pinchart 14324ca052b4SLaurent Pinchart source = uvc_entity_by_id(chain->dev, 14334ca052b4SLaurent Pinchart entity->baSourceID[0]); 14344ca052b4SLaurent Pinchart if (!source) { 14354ca052b4SLaurent Pinchart uvc_dbg(chain->dev, DESCR, 14364ca052b4SLaurent Pinchart "Can't connect extension unit %u in chain\n", 14374ca052b4SLaurent Pinchart forward->id); 14384ca052b4SLaurent Pinchart break; 14394ca052b4SLaurent Pinchart } 14404ca052b4SLaurent Pinchart 14414ca052b4SLaurent Pinchart forward->baSourceID[0] = source->id; 14424ca052b4SLaurent Pinchart } 14434ca052b4SLaurent Pinchart 14440c0d06caSMauro Carvalho Chehab list_add_tail(&forward->chain, &chain->entities); 14450c0d06caSMauro Carvalho Chehab if (!found) 14469e56380aSJoe Perches uvc_dbg_cont(PROBE, " (->"); 14470c0d06caSMauro Carvalho Chehab 14489e56380aSJoe Perches uvc_dbg_cont(PROBE, " XU %d", forward->id); 14490c0d06caSMauro Carvalho Chehab found = 1; 14500c0d06caSMauro Carvalho Chehab break; 14510c0d06caSMauro Carvalho Chehab 14520c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 14530c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 14540c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 14550c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 14560c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_IS_ITERM(forward)) { 14579e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 14589e56380aSJoe Perches "Unsupported input terminal %u\n", 1459ed4c5fa4SRicardo Ribalda forward->id); 14600c0d06caSMauro Carvalho Chehab return -EINVAL; 14610c0d06caSMauro Carvalho Chehab } 14620c0d06caSMauro Carvalho Chehab 14634ca052b4SLaurent Pinchart if (UVC_ENTITY_IS_OTERM(entity)) { 14644ca052b4SLaurent Pinchart uvc_dbg(chain->dev, DESCR, 14654ca052b4SLaurent Pinchart "Unsupported connection between output terminals %u and %u\n", 14664ca052b4SLaurent Pinchart entity->id, forward->id); 14674ca052b4SLaurent Pinchart break; 14684ca052b4SLaurent Pinchart } 14694ca052b4SLaurent Pinchart 14700c0d06caSMauro Carvalho Chehab list_add_tail(&forward->chain, &chain->entities); 14710c0d06caSMauro Carvalho Chehab if (!found) 14729e56380aSJoe Perches uvc_dbg_cont(PROBE, " (->"); 14730c0d06caSMauro Carvalho Chehab 14749e56380aSJoe Perches uvc_dbg_cont(PROBE, " OT %d", forward->id); 14750c0d06caSMauro Carvalho Chehab found = 1; 14760c0d06caSMauro Carvalho Chehab break; 14770c0d06caSMauro Carvalho Chehab } 14780c0d06caSMauro Carvalho Chehab } 14790c0d06caSMauro Carvalho Chehab if (found) 14809e56380aSJoe Perches uvc_dbg_cont(PROBE, ")"); 14810c0d06caSMauro Carvalho Chehab 14820c0d06caSMauro Carvalho Chehab return 0; 14830c0d06caSMauro Carvalho Chehab } 14840c0d06caSMauro Carvalho Chehab 14850c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_backward(struct uvc_video_chain *chain, 14860c0d06caSMauro Carvalho Chehab struct uvc_entity **_entity) 14870c0d06caSMauro Carvalho Chehab { 14880c0d06caSMauro Carvalho Chehab struct uvc_entity *entity = *_entity; 14890c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 14900c0d06caSMauro Carvalho Chehab int id = -EINVAL, i; 14910c0d06caSMauro Carvalho Chehab 14920c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(entity)) { 14930c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 14940c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 14950c0d06caSMauro Carvalho Chehab id = entity->baSourceID[0]; 14960c0d06caSMauro Carvalho Chehab break; 14970c0d06caSMauro Carvalho Chehab 14980c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 14990c0d06caSMauro Carvalho Chehab /* Single-input selector units are ignored. */ 15000c0d06caSMauro Carvalho Chehab if (entity->bNrInPins == 1) { 15010c0d06caSMauro Carvalho Chehab id = entity->baSourceID[0]; 15020c0d06caSMauro Carvalho Chehab break; 15030c0d06caSMauro Carvalho Chehab } 15040c0d06caSMauro Carvalho Chehab 15059e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- IT"); 15060c0d06caSMauro Carvalho Chehab 15070c0d06caSMauro Carvalho Chehab chain->selector = entity; 15080c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->bNrInPins; ++i) { 15090c0d06caSMauro Carvalho Chehab id = entity->baSourceID[i]; 15100c0d06caSMauro Carvalho Chehab term = uvc_entity_by_id(chain->dev, id); 15110c0d06caSMauro Carvalho Chehab if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { 15129e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 1513ed4c5fa4SRicardo Ribalda "Selector unit %d input %d isn't connected to an input terminal\n", 1514ed4c5fa4SRicardo Ribalda entity->id, i); 15150c0d06caSMauro Carvalho Chehab return -1; 15160c0d06caSMauro Carvalho Chehab } 15170c0d06caSMauro Carvalho Chehab 151868035c80SWill Deacon if (term->chain.next || term->chain.prev) { 15199e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 15209e56380aSJoe Perches "Found reference to entity %d already in chain\n", 152168035c80SWill Deacon term->id); 152268035c80SWill Deacon return -EINVAL; 152368035c80SWill Deacon } 152468035c80SWill Deacon 15259e56380aSJoe Perches uvc_dbg_cont(PROBE, " %d", term->id); 15260c0d06caSMauro Carvalho Chehab 15270c0d06caSMauro Carvalho Chehab list_add_tail(&term->chain, &chain->entities); 15280c0d06caSMauro Carvalho Chehab uvc_scan_chain_forward(chain, term, entity); 15290c0d06caSMauro Carvalho Chehab } 15300c0d06caSMauro Carvalho Chehab 15319e56380aSJoe Perches uvc_dbg_cont(PROBE, "\n"); 15320c0d06caSMauro Carvalho Chehab 15330c0d06caSMauro Carvalho Chehab id = 0; 15340c0d06caSMauro Carvalho Chehab break; 15350c0d06caSMauro Carvalho Chehab 15360c0d06caSMauro Carvalho Chehab case UVC_ITT_VENDOR_SPECIFIC: 15370c0d06caSMauro Carvalho Chehab case UVC_ITT_CAMERA: 15380c0d06caSMauro Carvalho Chehab case UVC_ITT_MEDIA_TRANSPORT_INPUT: 15390c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 15400c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 15410c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 15420c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 15430c0d06caSMauro Carvalho Chehab id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0; 15440c0d06caSMauro Carvalho Chehab break; 15450c0d06caSMauro Carvalho Chehab } 15460c0d06caSMauro Carvalho Chehab 15470c0d06caSMauro Carvalho Chehab if (id <= 0) { 15480c0d06caSMauro Carvalho Chehab *_entity = NULL; 15490c0d06caSMauro Carvalho Chehab return id; 15500c0d06caSMauro Carvalho Chehab } 15510c0d06caSMauro Carvalho Chehab 15520c0d06caSMauro Carvalho Chehab entity = uvc_entity_by_id(chain->dev, id); 15530c0d06caSMauro Carvalho Chehab if (entity == NULL) { 15549e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 15559e56380aSJoe Perches "Found reference to unknown entity %d\n", id); 15560c0d06caSMauro Carvalho Chehab return -EINVAL; 15570c0d06caSMauro Carvalho Chehab } 15580c0d06caSMauro Carvalho Chehab 15590c0d06caSMauro Carvalho Chehab *_entity = entity; 15600c0d06caSMauro Carvalho Chehab return 0; 15610c0d06caSMauro Carvalho Chehab } 15620c0d06caSMauro Carvalho Chehab 15630c0d06caSMauro Carvalho Chehab static int uvc_scan_chain(struct uvc_video_chain *chain, 15640c0d06caSMauro Carvalho Chehab struct uvc_entity *term) 15650c0d06caSMauro Carvalho Chehab { 15660c0d06caSMauro Carvalho Chehab struct uvc_entity *entity, *prev; 15670c0d06caSMauro Carvalho Chehab 15689e56380aSJoe Perches uvc_dbg(chain->dev, PROBE, "Scanning UVC chain:"); 15690c0d06caSMauro Carvalho Chehab 15700c0d06caSMauro Carvalho Chehab entity = term; 15710c0d06caSMauro Carvalho Chehab prev = NULL; 15720c0d06caSMauro Carvalho Chehab 15730c0d06caSMauro Carvalho Chehab while (entity != NULL) { 15740c0d06caSMauro Carvalho Chehab /* Entity must not be part of an existing chain */ 15750c0d06caSMauro Carvalho Chehab if (entity->chain.next || entity->chain.prev) { 15769e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 15779e56380aSJoe Perches "Found reference to entity %d already in chain\n", 1578ed4c5fa4SRicardo Ribalda entity->id); 15790c0d06caSMauro Carvalho Chehab return -EINVAL; 15800c0d06caSMauro Carvalho Chehab } 15810c0d06caSMauro Carvalho Chehab 15820c0d06caSMauro Carvalho Chehab /* Process entity */ 15830c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_entity(chain, entity) < 0) 15840c0d06caSMauro Carvalho Chehab return -EINVAL; 15850c0d06caSMauro Carvalho Chehab 15860c0d06caSMauro Carvalho Chehab /* Forward scan */ 15870c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_forward(chain, entity, prev) < 0) 15880c0d06caSMauro Carvalho Chehab return -EINVAL; 15890c0d06caSMauro Carvalho Chehab 15900c0d06caSMauro Carvalho Chehab /* Backward scan */ 15910c0d06caSMauro Carvalho Chehab prev = entity; 15920c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_backward(chain, &entity) < 0) 15930c0d06caSMauro Carvalho Chehab return -EINVAL; 15940c0d06caSMauro Carvalho Chehab } 15950c0d06caSMauro Carvalho Chehab 15960c0d06caSMauro Carvalho Chehab return 0; 15970c0d06caSMauro Carvalho Chehab } 15980c0d06caSMauro Carvalho Chehab 15990c0d06caSMauro Carvalho Chehab static unsigned int uvc_print_terms(struct list_head *terms, u16 dir, 16000c0d06caSMauro Carvalho Chehab char *buffer) 16010c0d06caSMauro Carvalho Chehab { 16020c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 16030c0d06caSMauro Carvalho Chehab unsigned int nterms = 0; 16040c0d06caSMauro Carvalho Chehab char *p = buffer; 16050c0d06caSMauro Carvalho Chehab 16060c0d06caSMauro Carvalho Chehab list_for_each_entry(term, terms, chain) { 16070c0d06caSMauro Carvalho Chehab if (!UVC_ENTITY_IS_TERM(term) || 16080c0d06caSMauro Carvalho Chehab UVC_TERM_DIRECTION(term) != dir) 16090c0d06caSMauro Carvalho Chehab continue; 16100c0d06caSMauro Carvalho Chehab 16110c0d06caSMauro Carvalho Chehab if (nterms) 16120c0d06caSMauro Carvalho Chehab p += sprintf(p, ","); 16130c0d06caSMauro Carvalho Chehab if (++nterms >= 4) { 16140c0d06caSMauro Carvalho Chehab p += sprintf(p, "..."); 16150c0d06caSMauro Carvalho Chehab break; 16160c0d06caSMauro Carvalho Chehab } 16170c0d06caSMauro Carvalho Chehab p += sprintf(p, "%u", term->id); 16180c0d06caSMauro Carvalho Chehab } 16190c0d06caSMauro Carvalho Chehab 16200c0d06caSMauro Carvalho Chehab return p - buffer; 16210c0d06caSMauro Carvalho Chehab } 16220c0d06caSMauro Carvalho Chehab 16230c0d06caSMauro Carvalho Chehab static const char *uvc_print_chain(struct uvc_video_chain *chain) 16240c0d06caSMauro Carvalho Chehab { 16250c0d06caSMauro Carvalho Chehab static char buffer[43]; 16260c0d06caSMauro Carvalho Chehab char *p = buffer; 16270c0d06caSMauro Carvalho Chehab 16280c0d06caSMauro Carvalho Chehab p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p); 16290c0d06caSMauro Carvalho Chehab p += sprintf(p, " -> "); 16300c0d06caSMauro Carvalho Chehab uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p); 16310c0d06caSMauro Carvalho Chehab 16320c0d06caSMauro Carvalho Chehab return buffer; 16330c0d06caSMauro Carvalho Chehab } 16340c0d06caSMauro Carvalho Chehab 1635e950267aSHenrik Ingo static struct uvc_video_chain *uvc_alloc_chain(struct uvc_device *dev) 1636e950267aSHenrik Ingo { 1637e950267aSHenrik Ingo struct uvc_video_chain *chain; 1638e950267aSHenrik Ingo 1639e950267aSHenrik Ingo chain = kzalloc(sizeof(*chain), GFP_KERNEL); 1640e950267aSHenrik Ingo if (chain == NULL) 1641e950267aSHenrik Ingo return NULL; 1642e950267aSHenrik Ingo 1643e950267aSHenrik Ingo INIT_LIST_HEAD(&chain->entities); 1644e950267aSHenrik Ingo mutex_init(&chain->ctrl_mutex); 1645e950267aSHenrik Ingo chain->dev = dev; 1646e950267aSHenrik Ingo v4l2_prio_init(&chain->prio); 1647e950267aSHenrik Ingo 1648e950267aSHenrik Ingo return chain; 1649e950267aSHenrik Ingo } 1650e950267aSHenrik Ingo 1651e950267aSHenrik Ingo /* 1652e950267aSHenrik Ingo * Fallback heuristic for devices that don't connect units and terminals in a 1653e950267aSHenrik Ingo * valid chain. 1654e950267aSHenrik Ingo * 1655e950267aSHenrik Ingo * Some devices have invalid baSourceID references, causing uvc_scan_chain() 1656e950267aSHenrik Ingo * to fail, but if we just take the entities we can find and put them together 1657e950267aSHenrik Ingo * in the most sensible chain we can think of, turns out they do work anyway. 1658e950267aSHenrik Ingo * Note: This heuristic assumes there is a single chain. 1659e950267aSHenrik Ingo * 1660e950267aSHenrik Ingo * At the time of writing, devices known to have such a broken chain are 1661e950267aSHenrik Ingo * - Acer Integrated Camera (5986:055a) 1662e950267aSHenrik Ingo * - Realtek rtl157a7 (0bda:57a7) 1663e950267aSHenrik Ingo */ 1664e950267aSHenrik Ingo static int uvc_scan_fallback(struct uvc_device *dev) 1665e950267aSHenrik Ingo { 1666e950267aSHenrik Ingo struct uvc_video_chain *chain; 1667e950267aSHenrik Ingo struct uvc_entity *iterm = NULL; 1668e950267aSHenrik Ingo struct uvc_entity *oterm = NULL; 1669e950267aSHenrik Ingo struct uvc_entity *entity; 1670e950267aSHenrik Ingo struct uvc_entity *prev; 1671e950267aSHenrik Ingo 1672e950267aSHenrik Ingo /* 1673e950267aSHenrik Ingo * Start by locating the input and output terminals. We only support 1674e950267aSHenrik Ingo * devices with exactly one of each for now. 1675e950267aSHenrik Ingo */ 1676e950267aSHenrik Ingo list_for_each_entry(entity, &dev->entities, list) { 1677e950267aSHenrik Ingo if (UVC_ENTITY_IS_ITERM(entity)) { 1678e950267aSHenrik Ingo if (iterm) 1679e950267aSHenrik Ingo return -EINVAL; 1680e950267aSHenrik Ingo iterm = entity; 1681e950267aSHenrik Ingo } 1682e950267aSHenrik Ingo 1683e950267aSHenrik Ingo if (UVC_ENTITY_IS_OTERM(entity)) { 1684e950267aSHenrik Ingo if (oterm) 1685e950267aSHenrik Ingo return -EINVAL; 1686e950267aSHenrik Ingo oterm = entity; 1687e950267aSHenrik Ingo } 1688e950267aSHenrik Ingo } 1689e950267aSHenrik Ingo 1690e950267aSHenrik Ingo if (iterm == NULL || oterm == NULL) 1691e950267aSHenrik Ingo return -EINVAL; 1692e950267aSHenrik Ingo 1693e950267aSHenrik Ingo /* Allocate the chain and fill it. */ 1694e950267aSHenrik Ingo chain = uvc_alloc_chain(dev); 1695e950267aSHenrik Ingo if (chain == NULL) 1696e950267aSHenrik Ingo return -ENOMEM; 1697e950267aSHenrik Ingo 1698e950267aSHenrik Ingo if (uvc_scan_chain_entity(chain, oterm) < 0) 1699e950267aSHenrik Ingo goto error; 1700e950267aSHenrik Ingo 1701e950267aSHenrik Ingo prev = oterm; 1702e950267aSHenrik Ingo 1703e950267aSHenrik Ingo /* 1704e950267aSHenrik Ingo * Add all Processing and Extension Units with two pads. The order 1705e950267aSHenrik Ingo * doesn't matter much, use reverse list traversal to connect units in 1706e950267aSHenrik Ingo * UVC descriptor order as we build the chain from output to input. This 1707e950267aSHenrik Ingo * leads to units appearing in the order meant by the manufacturer for 1708e950267aSHenrik Ingo * the cameras known to require this heuristic. 1709e950267aSHenrik Ingo */ 1710e950267aSHenrik Ingo list_for_each_entry_reverse(entity, &dev->entities, list) { 1711e950267aSHenrik Ingo if (entity->type != UVC_VC_PROCESSING_UNIT && 1712e950267aSHenrik Ingo entity->type != UVC_VC_EXTENSION_UNIT) 1713e950267aSHenrik Ingo continue; 1714e950267aSHenrik Ingo 1715e950267aSHenrik Ingo if (entity->num_pads != 2) 1716e950267aSHenrik Ingo continue; 1717e950267aSHenrik Ingo 1718e950267aSHenrik Ingo if (uvc_scan_chain_entity(chain, entity) < 0) 1719e950267aSHenrik Ingo goto error; 1720e950267aSHenrik Ingo 1721e950267aSHenrik Ingo prev->baSourceID[0] = entity->id; 1722e950267aSHenrik Ingo prev = entity; 1723e950267aSHenrik Ingo } 1724e950267aSHenrik Ingo 1725e950267aSHenrik Ingo if (uvc_scan_chain_entity(chain, iterm) < 0) 1726e950267aSHenrik Ingo goto error; 1727e950267aSHenrik Ingo 1728e950267aSHenrik Ingo prev->baSourceID[0] = iterm->id; 1729e950267aSHenrik Ingo 1730e950267aSHenrik Ingo list_add_tail(&chain->list, &dev->chains); 1731e950267aSHenrik Ingo 17329e56380aSJoe Perches uvc_dbg(dev, PROBE, "Found a video chain by fallback heuristic (%s)\n", 1733e950267aSHenrik Ingo uvc_print_chain(chain)); 1734e950267aSHenrik Ingo 1735e950267aSHenrik Ingo return 0; 1736e950267aSHenrik Ingo 1737e950267aSHenrik Ingo error: 1738e950267aSHenrik Ingo kfree(chain); 1739e950267aSHenrik Ingo return -EINVAL; 1740e950267aSHenrik Ingo } 1741e950267aSHenrik Ingo 17420c0d06caSMauro Carvalho Chehab /* 17430c0d06caSMauro Carvalho Chehab * Scan the device for video chains and register video devices. 17440c0d06caSMauro Carvalho Chehab * 17450c0d06caSMauro Carvalho Chehab * Chains are scanned starting at their output terminals and walked backwards. 17460c0d06caSMauro Carvalho Chehab */ 17470c0d06caSMauro Carvalho Chehab static int uvc_scan_device(struct uvc_device *dev) 17480c0d06caSMauro Carvalho Chehab { 17490c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 17500c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 17510c0d06caSMauro Carvalho Chehab 17520c0d06caSMauro Carvalho Chehab list_for_each_entry(term, &dev->entities, list) { 17530c0d06caSMauro Carvalho Chehab if (!UVC_ENTITY_IS_OTERM(term)) 17540c0d06caSMauro Carvalho Chehab continue; 17550c0d06caSMauro Carvalho Chehab 1756699b9a86SLaurent Pinchart /* 1757699b9a86SLaurent Pinchart * If the terminal is already included in a chain, skip it. 17580c0d06caSMauro Carvalho Chehab * This can happen for chains that have multiple output 17590c0d06caSMauro Carvalho Chehab * terminals, where all output terminals beside the first one 17600c0d06caSMauro Carvalho Chehab * will be inserted in the chain in forward scans. 17610c0d06caSMauro Carvalho Chehab */ 17620c0d06caSMauro Carvalho Chehab if (term->chain.next || term->chain.prev) 17630c0d06caSMauro Carvalho Chehab continue; 17640c0d06caSMauro Carvalho Chehab 1765e950267aSHenrik Ingo chain = uvc_alloc_chain(dev); 17660c0d06caSMauro Carvalho Chehab if (chain == NULL) 17670c0d06caSMauro Carvalho Chehab return -ENOMEM; 17680c0d06caSMauro Carvalho Chehab 17698be8ec6eSLaurent Pinchart term->flags |= UVC_ENTITY_FLAG_DEFAULT; 17708be8ec6eSLaurent Pinchart 17710c0d06caSMauro Carvalho Chehab if (uvc_scan_chain(chain, term) < 0) { 17720c0d06caSMauro Carvalho Chehab kfree(chain); 17730c0d06caSMauro Carvalho Chehab continue; 17740c0d06caSMauro Carvalho Chehab } 17750c0d06caSMauro Carvalho Chehab 17769e56380aSJoe Perches uvc_dbg(dev, PROBE, "Found a valid video chain (%s)\n", 17770c0d06caSMauro Carvalho Chehab uvc_print_chain(chain)); 17780c0d06caSMauro Carvalho Chehab 17790c0d06caSMauro Carvalho Chehab list_add_tail(&chain->list, &dev->chains); 17800c0d06caSMauro Carvalho Chehab } 17810c0d06caSMauro Carvalho Chehab 1782e950267aSHenrik Ingo if (list_empty(&dev->chains)) 1783e950267aSHenrik Ingo uvc_scan_fallback(dev); 1784e950267aSHenrik Ingo 17850c0d06caSMauro Carvalho Chehab if (list_empty(&dev->chains)) { 178669df0954SRicardo Ribalda dev_info(&dev->udev->dev, "No valid video chain found.\n"); 17870c0d06caSMauro Carvalho Chehab return -1; 17880c0d06caSMauro Carvalho Chehab } 17890c0d06caSMauro Carvalho Chehab 17902886477fSRicardo Ribalda /* Add GPIO entity to the first chain. */ 17912886477fSRicardo Ribalda if (dev->gpio_unit) { 17922886477fSRicardo Ribalda chain = list_first_entry(&dev->chains, 17932886477fSRicardo Ribalda struct uvc_video_chain, list); 17942886477fSRicardo Ribalda list_add_tail(&dev->gpio_unit->chain, &chain->entities); 17952886477fSRicardo Ribalda } 17962886477fSRicardo Ribalda 17970c0d06caSMauro Carvalho Chehab return 0; 17980c0d06caSMauro Carvalho Chehab } 17990c0d06caSMauro Carvalho Chehab 18000c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 18010c0d06caSMauro Carvalho Chehab * Video device registration and unregistration 18020c0d06caSMauro Carvalho Chehab */ 18030c0d06caSMauro Carvalho Chehab 18040c0d06caSMauro Carvalho Chehab /* 18050c0d06caSMauro Carvalho Chehab * Delete the UVC device. 18060c0d06caSMauro Carvalho Chehab * 18070c0d06caSMauro Carvalho Chehab * Called by the kernel when the last reference to the uvc_device structure 18080c0d06caSMauro Carvalho Chehab * is released. 18090c0d06caSMauro Carvalho Chehab * 18100c0d06caSMauro Carvalho Chehab * As this function is called after or during disconnect(), all URBs have 1811ece41454SKieran Bingham * already been cancelled by the USB core. There is no need to kill the 18120c0d06caSMauro Carvalho Chehab * interrupt URB manually. 18130c0d06caSMauro Carvalho Chehab */ 18149d15cd95SGuennadi Liakhovetski static void uvc_delete(struct kref *kref) 18150c0d06caSMauro Carvalho Chehab { 18169d15cd95SGuennadi Liakhovetski struct uvc_device *dev = container_of(kref, struct uvc_device, ref); 18170c0d06caSMauro Carvalho Chehab struct list_head *p, *n; 18180c0d06caSMauro Carvalho Chehab 18190c0d06caSMauro Carvalho Chehab uvc_status_cleanup(dev); 18200c0d06caSMauro Carvalho Chehab uvc_ctrl_cleanup_device(dev); 18210c0d06caSMauro Carvalho Chehab 18222228d80dSTakashi Iwai usb_put_intf(dev->intf); 18232228d80dSTakashi Iwai usb_put_dev(dev->udev); 18242228d80dSTakashi Iwai 18250c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 18269832e155SJavier Martinez Canillas media_device_cleanup(&dev->mdev); 18270c0d06caSMauro Carvalho Chehab #endif 18280c0d06caSMauro Carvalho Chehab 18290c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->chains) { 18300c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 18312a8c1952SPedro Guilherme Siqueira Moreira 18320c0d06caSMauro Carvalho Chehab chain = list_entry(p, struct uvc_video_chain, list); 18330c0d06caSMauro Carvalho Chehab kfree(chain); 18340c0d06caSMauro Carvalho Chehab } 18350c0d06caSMauro Carvalho Chehab 18360c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->entities) { 18370c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 18382a8c1952SPedro Guilherme Siqueira Moreira 18390c0d06caSMauro Carvalho Chehab entity = list_entry(p, struct uvc_entity, list); 18400c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 18410c0d06caSMauro Carvalho Chehab uvc_mc_cleanup_entity(entity); 18420c0d06caSMauro Carvalho Chehab #endif 18430c0d06caSMauro Carvalho Chehab kfree(entity); 18440c0d06caSMauro Carvalho Chehab } 18450c0d06caSMauro Carvalho Chehab 18460c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->streams) { 18470c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming; 18482a8c1952SPedro Guilherme Siqueira Moreira 18490c0d06caSMauro Carvalho Chehab streaming = list_entry(p, struct uvc_streaming, list); 18500c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, 18510c0d06caSMauro Carvalho Chehab streaming->intf); 1852ece41454SKieran Bingham uvc_stream_delete(streaming); 18530c0d06caSMauro Carvalho Chehab } 18540c0d06caSMauro Carvalho Chehab 18550c0d06caSMauro Carvalho Chehab kfree(dev); 18560c0d06caSMauro Carvalho Chehab } 18570c0d06caSMauro Carvalho Chehab 18580c0d06caSMauro Carvalho Chehab static void uvc_release(struct video_device *vdev) 18590c0d06caSMauro Carvalho Chehab { 18600c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream = video_get_drvdata(vdev); 18610c0d06caSMauro Carvalho Chehab struct uvc_device *dev = stream->dev; 18620c0d06caSMauro Carvalho Chehab 18639d15cd95SGuennadi Liakhovetski kref_put(&dev->ref, uvc_delete); 18640c0d06caSMauro Carvalho Chehab } 18650c0d06caSMauro Carvalho Chehab 18660c0d06caSMauro Carvalho Chehab /* 18670c0d06caSMauro Carvalho Chehab * Unregister the video devices. 18680c0d06caSMauro Carvalho Chehab */ 18690c0d06caSMauro Carvalho Chehab static void uvc_unregister_video(struct uvc_device *dev) 18700c0d06caSMauro Carvalho Chehab { 18710c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 18720c0d06caSMauro Carvalho Chehab 18730c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 1874d8da7513SHans Verkuil if (!video_is_registered(&stream->vdev)) 18750c0d06caSMauro Carvalho Chehab continue; 18760c0d06caSMauro Carvalho Chehab 1877d8da7513SHans Verkuil video_unregister_device(&stream->vdev); 1878088ead25SGuennadi Liakhovetski video_unregister_device(&stream->meta.vdev); 18790c0d06caSMauro Carvalho Chehab 18800c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup_stream(stream); 18810c0d06caSMauro Carvalho Chehab } 188210e1fdb9SDaniel Axtens 188310e1fdb9SDaniel Axtens uvc_status_unregister(dev); 188410e1fdb9SDaniel Axtens 188510e1fdb9SDaniel Axtens if (dev->vdev.dev) 188610e1fdb9SDaniel Axtens v4l2_device_unregister(&dev->vdev); 188710e1fdb9SDaniel Axtens #ifdef CONFIG_MEDIA_CONTROLLER 188810e1fdb9SDaniel Axtens if (media_devnode_is_registered(dev->mdev.devnode)) 188910e1fdb9SDaniel Axtens media_device_unregister(&dev->mdev); 189010e1fdb9SDaniel Axtens #endif 18910c0d06caSMauro Carvalho Chehab } 18920c0d06caSMauro Carvalho Chehab 189331a96f4cSLaurent Pinchart int uvc_register_video_device(struct uvc_device *dev, 189431a96f4cSLaurent Pinchart struct uvc_streaming *stream, 189531a96f4cSLaurent Pinchart struct video_device *vdev, 189631a96f4cSLaurent Pinchart struct uvc_video_queue *queue, 189731a96f4cSLaurent Pinchart enum v4l2_buf_type type, 189831a96f4cSLaurent Pinchart const struct v4l2_file_operations *fops, 189931a96f4cSLaurent Pinchart const struct v4l2_ioctl_ops *ioctl_ops) 19000c0d06caSMauro Carvalho Chehab { 19010c0d06caSMauro Carvalho Chehab int ret; 19020c0d06caSMauro Carvalho Chehab 1903b83bba24SLaurent Pinchart /* Initialize the video buffers queue. */ 190431a96f4cSLaurent Pinchart ret = uvc_queue_init(queue, type, !uvc_no_drop_param); 1905b83bba24SLaurent Pinchart if (ret) 1906b83bba24SLaurent Pinchart return ret; 1907b83bba24SLaurent Pinchart 19080c0d06caSMauro Carvalho Chehab /* Register the device with V4L. */ 19090c0d06caSMauro Carvalho Chehab 191031a96f4cSLaurent Pinchart /* 191131a96f4cSLaurent Pinchart * We already hold a reference to dev->udev. The video device will be 19120c0d06caSMauro Carvalho Chehab * unregistered before the reference is released, so we don't need to 19130c0d06caSMauro Carvalho Chehab * get another one. 19140c0d06caSMauro Carvalho Chehab */ 19150c0d06caSMauro Carvalho Chehab vdev->v4l2_dev = &dev->vdev; 191631a96f4cSLaurent Pinchart vdev->fops = fops; 191731a96f4cSLaurent Pinchart vdev->ioctl_ops = ioctl_ops; 19180c0d06caSMauro Carvalho Chehab vdev->release = uvc_release; 19190550513cSLaurent Pinchart vdev->prio = &stream->chain->prio; 192031a96f4cSLaurent Pinchart if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 1921954f340fSHans Verkuil vdev->vfl_dir = VFL_DIR_TX; 192231a96f4cSLaurent Pinchart else 192331a96f4cSLaurent Pinchart vdev->vfl_dir = VFL_DIR_RX; 192494c53e26SLaurent Pinchart 192594c53e26SLaurent Pinchart switch (type) { 192694c53e26SLaurent Pinchart case V4L2_BUF_TYPE_VIDEO_CAPTURE: 192794c53e26SLaurent Pinchart default: 192894c53e26SLaurent Pinchart vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; 192994c53e26SLaurent Pinchart break; 193094c53e26SLaurent Pinchart case V4L2_BUF_TYPE_VIDEO_OUTPUT: 193194c53e26SLaurent Pinchart vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; 193294c53e26SLaurent Pinchart break; 1933088ead25SGuennadi Liakhovetski case V4L2_BUF_TYPE_META_CAPTURE: 1934088ead25SGuennadi Liakhovetski vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING; 1935088ead25SGuennadi Liakhovetski break; 193694c53e26SLaurent Pinchart } 193794c53e26SLaurent Pinchart 1938f66dcb32SRicardo Ribalda strscpy(vdev->name, dev->name, sizeof(vdev->name)); 19390c0d06caSMauro Carvalho Chehab 194031a96f4cSLaurent Pinchart /* 194131a96f4cSLaurent Pinchart * Set the driver data before calling video_register_device, otherwise 194231a96f4cSLaurent Pinchart * the file open() handler might race us. 19430c0d06caSMauro Carvalho Chehab */ 19440c0d06caSMauro Carvalho Chehab video_set_drvdata(vdev, stream); 19450c0d06caSMauro Carvalho Chehab 19467fbbbc78SHans Verkuil ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 19470c0d06caSMauro Carvalho Chehab if (ret < 0) { 194869df0954SRicardo Ribalda dev_err(&stream->intf->dev, 194969df0954SRicardo Ribalda "Failed to register %s device (%d).\n", 195031a96f4cSLaurent Pinchart v4l2_type_names[type], ret); 195131a96f4cSLaurent Pinchart return ret; 195231a96f4cSLaurent Pinchart } 195331a96f4cSLaurent Pinchart 195431a96f4cSLaurent Pinchart kref_get(&dev->ref); 195531a96f4cSLaurent Pinchart return 0; 195631a96f4cSLaurent Pinchart } 195731a96f4cSLaurent Pinchart 195831a96f4cSLaurent Pinchart static int uvc_register_video(struct uvc_device *dev, 195931a96f4cSLaurent Pinchart struct uvc_streaming *stream) 196031a96f4cSLaurent Pinchart { 196131a96f4cSLaurent Pinchart int ret; 196231a96f4cSLaurent Pinchart 196331a96f4cSLaurent Pinchart /* Initialize the streaming interface with default parameters. */ 196431a96f4cSLaurent Pinchart ret = uvc_video_init(stream); 196531a96f4cSLaurent Pinchart if (ret < 0) { 196669df0954SRicardo Ribalda dev_err(&stream->intf->dev, 196769df0954SRicardo Ribalda "Failed to initialize the device (%d).\n", ret); 19680c0d06caSMauro Carvalho Chehab return ret; 19690c0d06caSMauro Carvalho Chehab } 19700c0d06caSMauro Carvalho Chehab 1971f887e99aSLaurent Pinchart if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 1972088ead25SGuennadi Liakhovetski stream->chain->caps |= V4L2_CAP_VIDEO_CAPTURE 1973088ead25SGuennadi Liakhovetski | V4L2_CAP_META_CAPTURE; 1974f887e99aSLaurent Pinchart else 1975f887e99aSLaurent Pinchart stream->chain->caps |= V4L2_CAP_VIDEO_OUTPUT; 1976f887e99aSLaurent Pinchart 197731a96f4cSLaurent Pinchart uvc_debugfs_init_stream(stream); 197831a96f4cSLaurent Pinchart 197931a96f4cSLaurent Pinchart /* Register the device with V4L. */ 198031a96f4cSLaurent Pinchart return uvc_register_video_device(dev, stream, &stream->vdev, 198131a96f4cSLaurent Pinchart &stream->queue, stream->type, 198231a96f4cSLaurent Pinchart &uvc_fops, &uvc_ioctl_ops); 19830c0d06caSMauro Carvalho Chehab } 19840c0d06caSMauro Carvalho Chehab 19850c0d06caSMauro Carvalho Chehab /* 19860c0d06caSMauro Carvalho Chehab * Register all video devices in all chains. 19870c0d06caSMauro Carvalho Chehab */ 19880c0d06caSMauro Carvalho Chehab static int uvc_register_terms(struct uvc_device *dev, 19890c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain) 19900c0d06caSMauro Carvalho Chehab { 19910c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 19920c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 19930c0d06caSMauro Carvalho Chehab int ret; 19940c0d06caSMauro Carvalho Chehab 19950c0d06caSMauro Carvalho Chehab list_for_each_entry(term, &chain->entities, chain) { 19960c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) 19970c0d06caSMauro Carvalho Chehab continue; 19980c0d06caSMauro Carvalho Chehab 19990c0d06caSMauro Carvalho Chehab stream = uvc_stream_by_id(dev, term->id); 20000c0d06caSMauro Carvalho Chehab if (stream == NULL) { 200169df0954SRicardo Ribalda dev_info(&dev->udev->dev, 200269df0954SRicardo Ribalda "No streaming interface found for terminal %u.", 200369df0954SRicardo Ribalda term->id); 20040c0d06caSMauro Carvalho Chehab continue; 20050c0d06caSMauro Carvalho Chehab } 20060c0d06caSMauro Carvalho Chehab 20070c0d06caSMauro Carvalho Chehab stream->chain = chain; 20080c0d06caSMauro Carvalho Chehab ret = uvc_register_video(dev, stream); 20090c0d06caSMauro Carvalho Chehab if (ret < 0) 20100c0d06caSMauro Carvalho Chehab return ret; 20110c0d06caSMauro Carvalho Chehab 2012699b9a86SLaurent Pinchart /* 2013699b9a86SLaurent Pinchart * Register a metadata node, but ignore a possible failure, 2014088ead25SGuennadi Liakhovetski * complete registration of video nodes anyway. 2015088ead25SGuennadi Liakhovetski */ 2016088ead25SGuennadi Liakhovetski uvc_meta_register(stream); 2017088ead25SGuennadi Liakhovetski 2018d8da7513SHans Verkuil term->vdev = &stream->vdev; 20190c0d06caSMauro Carvalho Chehab } 20200c0d06caSMauro Carvalho Chehab 20210c0d06caSMauro Carvalho Chehab return 0; 20220c0d06caSMauro Carvalho Chehab } 20230c0d06caSMauro Carvalho Chehab 20240c0d06caSMauro Carvalho Chehab static int uvc_register_chains(struct uvc_device *dev) 20250c0d06caSMauro Carvalho Chehab { 20260c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 20270c0d06caSMauro Carvalho Chehab int ret; 20280c0d06caSMauro Carvalho Chehab 20290c0d06caSMauro Carvalho Chehab list_for_each_entry(chain, &dev->chains, list) { 20300c0d06caSMauro Carvalho Chehab ret = uvc_register_terms(dev, chain); 20310c0d06caSMauro Carvalho Chehab if (ret < 0) 20320c0d06caSMauro Carvalho Chehab return ret; 20330c0d06caSMauro Carvalho Chehab 20340c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 20350c0d06caSMauro Carvalho Chehab ret = uvc_mc_register_entities(chain); 20366689df06SColin Ian King if (ret < 0) 203769df0954SRicardo Ribalda dev_info(&dev->udev->dev, 20386689df06SColin Ian King "Failed to register entities (%d).\n", ret); 20390c0d06caSMauro Carvalho Chehab #endif 20400c0d06caSMauro Carvalho Chehab } 20410c0d06caSMauro Carvalho Chehab 20420c0d06caSMauro Carvalho Chehab return 0; 20430c0d06caSMauro Carvalho Chehab } 20440c0d06caSMauro Carvalho Chehab 20450c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 20460c0d06caSMauro Carvalho Chehab * USB probe, disconnect, suspend and resume 20470c0d06caSMauro Carvalho Chehab */ 20480c0d06caSMauro Carvalho Chehab 20493a03284dSLaurent Pinchart static const struct uvc_device_info uvc_quirk_none = { 0 }; 20503bc85817SGuennadi Liakhovetski 20510c0d06caSMauro Carvalho Chehab static int uvc_probe(struct usb_interface *intf, 20520c0d06caSMauro Carvalho Chehab const struct usb_device_id *id) 20530c0d06caSMauro Carvalho Chehab { 20540c0d06caSMauro Carvalho Chehab struct usb_device *udev = interface_to_usbdev(intf); 20550c0d06caSMauro Carvalho Chehab struct uvc_device *dev; 20563bc85817SGuennadi Liakhovetski const struct uvc_device_info *info = 20573bc85817SGuennadi Liakhovetski (const struct uvc_device_info *)id->driver_info; 2058e7b09f18SPeter Boström int function; 20590c0d06caSMauro Carvalho Chehab int ret; 20600c0d06caSMauro Carvalho Chehab 20610c0d06caSMauro Carvalho Chehab /* Allocate memory for the device and initialize it. */ 2062f14d4988SLaurent Pinchart dev = kzalloc(sizeof(*dev), GFP_KERNEL); 2063f14d4988SLaurent Pinchart if (dev == NULL) 20640c0d06caSMauro Carvalho Chehab return -ENOMEM; 20650c0d06caSMauro Carvalho Chehab 20660c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->entities); 20670c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->chains); 20680c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->streams); 20699d15cd95SGuennadi Liakhovetski kref_init(&dev->ref); 20700c0d06caSMauro Carvalho Chehab atomic_set(&dev->nmappings, 0); 207117706f56SLaurent Pinchart mutex_init(&dev->lock); 20720c0d06caSMauro Carvalho Chehab 20730c0d06caSMauro Carvalho Chehab dev->udev = usb_get_dev(udev); 20740c0d06caSMauro Carvalho Chehab dev->intf = usb_get_intf(intf); 20750c0d06caSMauro Carvalho Chehab dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; 20763a03284dSLaurent Pinchart dev->info = info ? info : &uvc_quirk_none; 20773a03284dSLaurent Pinchart dev->quirks = uvc_quirks_param == -1 20783a03284dSLaurent Pinchart ? dev->info->quirks : uvc_quirks_param; 20790c0d06caSMauro Carvalho Chehab 2080ed4c5fa4SRicardo Ribalda if (id->idVendor && id->idProduct) 20819e56380aSJoe Perches uvc_dbg(dev, PROBE, "Probing known UVC device %s (%04x:%04x)\n", 2082ed4c5fa4SRicardo Ribalda udev->devpath, id->idVendor, id->idProduct); 2083ed4c5fa4SRicardo Ribalda else 20849e56380aSJoe Perches uvc_dbg(dev, PROBE, "Probing generic UVC device %s\n", 20859e56380aSJoe Perches udev->devpath); 2086ed4c5fa4SRicardo Ribalda 20870c0d06caSMauro Carvalho Chehab if (udev->product != NULL) 2088c0decac1SMauro Carvalho Chehab strscpy(dev->name, udev->product, sizeof(dev->name)); 20890c0d06caSMauro Carvalho Chehab else 2090f14d4988SLaurent Pinchart snprintf(dev->name, sizeof(dev->name), 20910c0d06caSMauro Carvalho Chehab "UVC Camera (%04x:%04x)", 20920c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idVendor), 20930c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idProduct)); 20940c0d06caSMauro Carvalho Chehab 2095e7b09f18SPeter Boström /* 2096e7b09f18SPeter Boström * Add iFunction or iInterface to names when available as additional 2097e7b09f18SPeter Boström * distinguishers between interfaces. iFunction is prioritized over 2098e7b09f18SPeter Boström * iInterface which matches Windows behavior at the point of writing. 2099e7b09f18SPeter Boström */ 2100e7b09f18SPeter Boström if (intf->intf_assoc && intf->intf_assoc->iFunction != 0) 2101e7b09f18SPeter Boström function = intf->intf_assoc->iFunction; 2102e7b09f18SPeter Boström else 2103e7b09f18SPeter Boström function = intf->cur_altsetting->desc.iInterface; 2104e7b09f18SPeter Boström if (function != 0) { 2105e7b09f18SPeter Boström size_t len; 2106e7b09f18SPeter Boström 2107e7b09f18SPeter Boström strlcat(dev->name, ": ", sizeof(dev->name)); 2108e7b09f18SPeter Boström len = strlen(dev->name); 2109e7b09f18SPeter Boström usb_string(udev, function, dev->name + len, 2110e7b09f18SPeter Boström sizeof(dev->name) - len); 2111e7b09f18SPeter Boström } 2112e7b09f18SPeter Boström 21138c279e93SLaurent Pinchart /* Initialize the media device. */ 21148c279e93SLaurent Pinchart #ifdef CONFIG_MEDIA_CONTROLLER 21158c279e93SLaurent Pinchart dev->mdev.dev = &intf->dev; 21168c279e93SLaurent Pinchart strscpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model)); 21178c279e93SLaurent Pinchart if (udev->serial) 21188c279e93SLaurent Pinchart strscpy(dev->mdev.serial, udev->serial, 21198c279e93SLaurent Pinchart sizeof(dev->mdev.serial)); 21208c279e93SLaurent Pinchart usb_make_path(udev, dev->mdev.bus_info, sizeof(dev->mdev.bus_info)); 21218c279e93SLaurent Pinchart dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); 21228c279e93SLaurent Pinchart media_device_init(&dev->mdev); 21238c279e93SLaurent Pinchart 21248c279e93SLaurent Pinchart dev->vdev.mdev = &dev->mdev; 21258c279e93SLaurent Pinchart #endif 21268c279e93SLaurent Pinchart 21270c0d06caSMauro Carvalho Chehab /* Parse the Video Class control descriptor. */ 21280c0d06caSMauro Carvalho Chehab if (uvc_parse_control(dev) < 0) { 21299e56380aSJoe Perches uvc_dbg(dev, PROBE, "Unable to parse UVC descriptors\n"); 21300c0d06caSMauro Carvalho Chehab goto error; 21310c0d06caSMauro Carvalho Chehab } 21320c0d06caSMauro Carvalho Chehab 21332886477fSRicardo Ribalda /* Parse the associated GPIOs. */ 21342886477fSRicardo Ribalda if (uvc_gpio_parse(dev) < 0) { 21359e56380aSJoe Perches uvc_dbg(dev, PROBE, "Unable to parse UVC GPIOs\n"); 21362886477fSRicardo Ribalda goto error; 21372886477fSRicardo Ribalda } 21382886477fSRicardo Ribalda 213969df0954SRicardo Ribalda dev_info(&dev->udev->dev, "Found UVC %u.%02x device %s (%04x:%04x)\n", 21400c0d06caSMauro Carvalho Chehab dev->uvc_version >> 8, dev->uvc_version & 0xff, 21410c0d06caSMauro Carvalho Chehab udev->product ? udev->product : "<unnamed>", 21420c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idVendor), 21430c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idProduct)); 21440c0d06caSMauro Carvalho Chehab 21453a03284dSLaurent Pinchart if (dev->quirks != dev->info->quirks) { 214669df0954SRicardo Ribalda dev_info(&dev->udev->dev, 214769df0954SRicardo Ribalda "Forcing device quirks to 0x%x by module parameter for testing purpose.\n", 214869df0954SRicardo Ribalda dev->quirks); 214969df0954SRicardo Ribalda dev_info(&dev->udev->dev, 2150e3a0f556SJonathan Neuschäfer "Please report required quirks to the linux-media mailing list.\n"); 21510c0d06caSMauro Carvalho Chehab } 21520c0d06caSMauro Carvalho Chehab 2153b400b6f2SLaurent Pinchart if (dev->info->uvc_version) { 2154b400b6f2SLaurent Pinchart dev->uvc_version = dev->info->uvc_version; 215569df0954SRicardo Ribalda dev_info(&dev->udev->dev, "Forcing UVC version to %u.%02x\n", 2156b400b6f2SLaurent Pinchart dev->uvc_version >> 8, dev->uvc_version & 0xff); 2157b400b6f2SLaurent Pinchart } 2158b400b6f2SLaurent Pinchart 21598c279e93SLaurent Pinchart /* Register the V4L2 device. */ 21600c0d06caSMauro Carvalho Chehab if (v4l2_device_register(&intf->dev, &dev->vdev) < 0) 21610c0d06caSMauro Carvalho Chehab goto error; 21620c0d06caSMauro Carvalho Chehab 21630c0d06caSMauro Carvalho Chehab /* Scan the device for video chains. */ 21640c0d06caSMauro Carvalho Chehab if (uvc_scan_device(dev) < 0) 21650c0d06caSMauro Carvalho Chehab goto error; 21660c0d06caSMauro Carvalho Chehab 2167866c6bddSRicardo Ribalda /* Initialize controls. */ 2168866c6bddSRicardo Ribalda if (uvc_ctrl_init_device(dev) < 0) 2169866c6bddSRicardo Ribalda goto error; 2170866c6bddSRicardo Ribalda 21710c0d06caSMauro Carvalho Chehab /* Register video device nodes. */ 21720c0d06caSMauro Carvalho Chehab if (uvc_register_chains(dev) < 0) 21730c0d06caSMauro Carvalho Chehab goto error; 21740c0d06caSMauro Carvalho Chehab 21759832e155SJavier Martinez Canillas #ifdef CONFIG_MEDIA_CONTROLLER 21769832e155SJavier Martinez Canillas /* Register the media device node */ 21779832e155SJavier Martinez Canillas if (media_device_register(&dev->mdev) < 0) 21789832e155SJavier Martinez Canillas goto error; 21799832e155SJavier Martinez Canillas #endif 21800c0d06caSMauro Carvalho Chehab /* Save our data pointer in the interface data. */ 21810c0d06caSMauro Carvalho Chehab usb_set_intfdata(intf, dev); 21820c0d06caSMauro Carvalho Chehab 21830c0d06caSMauro Carvalho Chehab /* Initialize the interrupt URB. */ 21847b78a846SPedro Guilherme Siqueira Moreira ret = uvc_status_init(dev); 21857b78a846SPedro Guilherme Siqueira Moreira if (ret < 0) { 218669df0954SRicardo Ribalda dev_info(&dev->udev->dev, 218769df0954SRicardo Ribalda "Unable to initialize the status endpoint (%d), status interrupt will not be supported.\n", 218869df0954SRicardo Ribalda ret); 21890c0d06caSMauro Carvalho Chehab } 21900c0d06caSMauro Carvalho Chehab 21912886477fSRicardo Ribalda ret = uvc_gpio_init_irq(dev); 21922886477fSRicardo Ribalda if (ret < 0) { 21932886477fSRicardo Ribalda dev_err(&dev->udev->dev, 21942886477fSRicardo Ribalda "Unable to request privacy GPIO IRQ (%d)\n", ret); 21952886477fSRicardo Ribalda goto error; 21962886477fSRicardo Ribalda } 21972886477fSRicardo Ribalda 21989e56380aSJoe Perches uvc_dbg(dev, PROBE, "UVC device initialized\n"); 21990c0d06caSMauro Carvalho Chehab usb_enable_autosuspend(udev); 22000c0d06caSMauro Carvalho Chehab return 0; 22010c0d06caSMauro Carvalho Chehab 22020c0d06caSMauro Carvalho Chehab error: 22030c0d06caSMauro Carvalho Chehab uvc_unregister_video(dev); 2204f9ffcb0aSPhilipp Zabel kref_put(&dev->ref, uvc_delete); 22050c0d06caSMauro Carvalho Chehab return -ENODEV; 22060c0d06caSMauro Carvalho Chehab } 22070c0d06caSMauro Carvalho Chehab 22080c0d06caSMauro Carvalho Chehab static void uvc_disconnect(struct usb_interface *intf) 22090c0d06caSMauro Carvalho Chehab { 22100c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 22110c0d06caSMauro Carvalho Chehab 2212699b9a86SLaurent Pinchart /* 2213699b9a86SLaurent Pinchart * Set the USB interface data to NULL. This can be done outside the 22140c0d06caSMauro Carvalho Chehab * lock, as there's no other reader. 22150c0d06caSMauro Carvalho Chehab */ 22160c0d06caSMauro Carvalho Chehab usb_set_intfdata(intf, NULL); 22170c0d06caSMauro Carvalho Chehab 22180c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 22190c0d06caSMauro Carvalho Chehab UVC_SC_VIDEOSTREAMING) 22200c0d06caSMauro Carvalho Chehab return; 22210c0d06caSMauro Carvalho Chehab 22220c0d06caSMauro Carvalho Chehab uvc_unregister_video(dev); 2223f9ffcb0aSPhilipp Zabel kref_put(&dev->ref, uvc_delete); 22240c0d06caSMauro Carvalho Chehab } 22250c0d06caSMauro Carvalho Chehab 22260c0d06caSMauro Carvalho Chehab static int uvc_suspend(struct usb_interface *intf, pm_message_t message) 22270c0d06caSMauro Carvalho Chehab { 22280c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 22290c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 22300c0d06caSMauro Carvalho Chehab 22319e56380aSJoe Perches uvc_dbg(dev, SUSPEND, "Suspending interface %u\n", 22320c0d06caSMauro Carvalho Chehab intf->cur_altsetting->desc.bInterfaceNumber); 22330c0d06caSMauro Carvalho Chehab 22340c0d06caSMauro Carvalho Chehab /* Controls are cached on the fly so they don't need to be saved. */ 22350c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 223617706f56SLaurent Pinchart UVC_SC_VIDEOCONTROL) { 223717706f56SLaurent Pinchart mutex_lock(&dev->lock); 223817706f56SLaurent Pinchart if (dev->users) 223917706f56SLaurent Pinchart uvc_status_stop(dev); 224017706f56SLaurent Pinchart mutex_unlock(&dev->lock); 224117706f56SLaurent Pinchart return 0; 224217706f56SLaurent Pinchart } 22430c0d06caSMauro Carvalho Chehab 22440c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 22450c0d06caSMauro Carvalho Chehab if (stream->intf == intf) 22460c0d06caSMauro Carvalho Chehab return uvc_video_suspend(stream); 22470c0d06caSMauro Carvalho Chehab } 22480c0d06caSMauro Carvalho Chehab 22499e56380aSJoe Perches uvc_dbg(dev, SUSPEND, 22509e56380aSJoe Perches "Suspend: video streaming USB interface mismatch\n"); 22510c0d06caSMauro Carvalho Chehab return -EINVAL; 22520c0d06caSMauro Carvalho Chehab } 22530c0d06caSMauro Carvalho Chehab 22540c0d06caSMauro Carvalho Chehab static int __uvc_resume(struct usb_interface *intf, int reset) 22550c0d06caSMauro Carvalho Chehab { 22560c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 22570c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 2258b83bba24SLaurent Pinchart int ret = 0; 22590c0d06caSMauro Carvalho Chehab 22609e56380aSJoe Perches uvc_dbg(dev, SUSPEND, "Resuming interface %u\n", 22610c0d06caSMauro Carvalho Chehab intf->cur_altsetting->desc.bInterfaceNumber); 22620c0d06caSMauro Carvalho Chehab 22630c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 22640c0d06caSMauro Carvalho Chehab UVC_SC_VIDEOCONTROL) { 226517706f56SLaurent Pinchart if (reset) { 226617e1319fSWilliam Manley ret = uvc_ctrl_restore_values(dev); 22670c0d06caSMauro Carvalho Chehab if (ret < 0) 22680c0d06caSMauro Carvalho Chehab return ret; 22690c0d06caSMauro Carvalho Chehab } 22700c0d06caSMauro Carvalho Chehab 227117706f56SLaurent Pinchart mutex_lock(&dev->lock); 227217706f56SLaurent Pinchart if (dev->users) 227317706f56SLaurent Pinchart ret = uvc_status_start(dev, GFP_NOIO); 227417706f56SLaurent Pinchart mutex_unlock(&dev->lock); 227517706f56SLaurent Pinchart 227617706f56SLaurent Pinchart return ret; 22770c0d06caSMauro Carvalho Chehab } 22780c0d06caSMauro Carvalho Chehab 22790c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 2280b83bba24SLaurent Pinchart if (stream->intf == intf) { 2281b83bba24SLaurent Pinchart ret = uvc_video_resume(stream, reset); 2282b83bba24SLaurent Pinchart if (ret < 0) 22830da4ab98SLaurent Pinchart uvc_queue_streamoff(&stream->queue, 22840da4ab98SLaurent Pinchart stream->queue.queue.type); 2285b83bba24SLaurent Pinchart return ret; 2286b83bba24SLaurent Pinchart } 22870c0d06caSMauro Carvalho Chehab } 22880c0d06caSMauro Carvalho Chehab 22899e56380aSJoe Perches uvc_dbg(dev, SUSPEND, 22909e56380aSJoe Perches "Resume: video streaming USB interface mismatch\n"); 22910c0d06caSMauro Carvalho Chehab return -EINVAL; 22920c0d06caSMauro Carvalho Chehab } 22930c0d06caSMauro Carvalho Chehab 22940c0d06caSMauro Carvalho Chehab static int uvc_resume(struct usb_interface *intf) 22950c0d06caSMauro Carvalho Chehab { 22960c0d06caSMauro Carvalho Chehab return __uvc_resume(intf, 0); 22970c0d06caSMauro Carvalho Chehab } 22980c0d06caSMauro Carvalho Chehab 22990c0d06caSMauro Carvalho Chehab static int uvc_reset_resume(struct usb_interface *intf) 23000c0d06caSMauro Carvalho Chehab { 23010c0d06caSMauro Carvalho Chehab return __uvc_resume(intf, 1); 23020c0d06caSMauro Carvalho Chehab } 23030c0d06caSMauro Carvalho Chehab 23040c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 23050c0d06caSMauro Carvalho Chehab * Module parameters 23060c0d06caSMauro Carvalho Chehab */ 23070c0d06caSMauro Carvalho Chehab 2308e4dca7b7SKees Cook static int uvc_clock_param_get(char *buffer, const struct kernel_param *kp) 23090c0d06caSMauro Carvalho Chehab { 23100c0d06caSMauro Carvalho Chehab if (uvc_clock_param == CLOCK_MONOTONIC) 23110c0d06caSMauro Carvalho Chehab return sprintf(buffer, "CLOCK_MONOTONIC"); 23120c0d06caSMauro Carvalho Chehab else 23130c0d06caSMauro Carvalho Chehab return sprintf(buffer, "CLOCK_REALTIME"); 23140c0d06caSMauro Carvalho Chehab } 23150c0d06caSMauro Carvalho Chehab 2316e4dca7b7SKees Cook static int uvc_clock_param_set(const char *val, const struct kernel_param *kp) 23170c0d06caSMauro Carvalho Chehab { 23180c0d06caSMauro Carvalho Chehab if (strncasecmp(val, "clock_", strlen("clock_")) == 0) 23190c0d06caSMauro Carvalho Chehab val += strlen("clock_"); 23200c0d06caSMauro Carvalho Chehab 23210c0d06caSMauro Carvalho Chehab if (strcasecmp(val, "monotonic") == 0) 23220c0d06caSMauro Carvalho Chehab uvc_clock_param = CLOCK_MONOTONIC; 23230c0d06caSMauro Carvalho Chehab else if (strcasecmp(val, "realtime") == 0) 23240c0d06caSMauro Carvalho Chehab uvc_clock_param = CLOCK_REALTIME; 23250c0d06caSMauro Carvalho Chehab else 23260c0d06caSMauro Carvalho Chehab return -EINVAL; 23270c0d06caSMauro Carvalho Chehab 23280c0d06caSMauro Carvalho Chehab return 0; 23290c0d06caSMauro Carvalho Chehab } 23300c0d06caSMauro Carvalho Chehab 23310c0d06caSMauro Carvalho Chehab module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get, 23320ce75d5eSPedro Guilherme Siqueira Moreira &uvc_clock_param, 0644); 23330c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(clock, "Video buffers timestamp clock"); 23340ce75d5eSPedro Guilherme Siqueira Moreira module_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, 0644); 23355d0fd3c8SLaurent Pinchart MODULE_PARM_DESC(hwtimestamps, "Use hardware timestamps"); 23360ce75d5eSPedro Guilherme Siqueira Moreira module_param_named(nodrop, uvc_no_drop_param, uint, 0644); 23370c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); 23380ce75d5eSPedro Guilherme Siqueira Moreira module_param_named(quirks, uvc_quirks_param, uint, 0644); 23390c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(quirks, "Forced device quirks"); 23400ce75d5eSPedro Guilherme Siqueira Moreira module_param_named(trace, uvc_dbg_param, uint, 0644); 23410c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(trace, "Trace level bitmask"); 23420ce75d5eSPedro Guilherme Siqueira Moreira module_param_named(timeout, uvc_timeout_param, uint, 0644); 23430c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); 23440c0d06caSMauro Carvalho Chehab 23450c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 23460c0d06caSMauro Carvalho Chehab * Driver initialization and cleanup 23470c0d06caSMauro Carvalho Chehab */ 23480c0d06caSMauro Carvalho Chehab 234938207560SRicardo Ribalda static const struct uvc_menu_info power_line_frequency_controls_limited[] = { 235038207560SRicardo Ribalda { 1, "50 Hz" }, 235138207560SRicardo Ribalda { 2, "60 Hz" }, 235238207560SRicardo Ribalda }; 235338207560SRicardo Ribalda 235438207560SRicardo Ribalda static const struct uvc_control_mapping uvc_ctrl_power_line_mapping_limited = { 235538207560SRicardo Ribalda .id = V4L2_CID_POWER_LINE_FREQUENCY, 235638207560SRicardo Ribalda .entity = UVC_GUID_UVC_PROCESSING, 235738207560SRicardo Ribalda .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, 235838207560SRicardo Ribalda .size = 2, 235938207560SRicardo Ribalda .offset = 0, 236038207560SRicardo Ribalda .v4l2_type = V4L2_CTRL_TYPE_MENU, 236138207560SRicardo Ribalda .data_type = UVC_CTRL_DATA_TYPE_ENUM, 236238207560SRicardo Ribalda .menu_info = power_line_frequency_controls_limited, 236338207560SRicardo Ribalda .menu_count = ARRAY_SIZE(power_line_frequency_controls_limited), 236438207560SRicardo Ribalda }; 236538207560SRicardo Ribalda 236638207560SRicardo Ribalda static const struct uvc_device_info uvc_ctrl_power_line_limited = { 236738207560SRicardo Ribalda .mappings = (const struct uvc_control_mapping *[]) { 236838207560SRicardo Ribalda &uvc_ctrl_power_line_mapping_limited, 236938207560SRicardo Ribalda NULL, /* Sentinel */ 237038207560SRicardo Ribalda }, 237138207560SRicardo Ribalda }; 237238207560SRicardo Ribalda 23733bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_probe_minmax = { 23743bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_PROBE_MINMAX, 23753bc85817SGuennadi Liakhovetski }; 23763bc85817SGuennadi Liakhovetski 23773bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_fix_bandwidth = { 23783bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_FIX_BANDWIDTH, 23793bc85817SGuennadi Liakhovetski }; 23803bc85817SGuennadi Liakhovetski 23813bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_probe_def = { 23823bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_PROBE_DEF, 23833bc85817SGuennadi Liakhovetski }; 23843bc85817SGuennadi Liakhovetski 23853bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_stream_no_fid = { 23863bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_STREAM_NO_FID, 23873bc85817SGuennadi Liakhovetski }; 23883bc85817SGuennadi Liakhovetski 23893bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_force_y8 = { 23903bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_FORCE_Y8, 23913bc85817SGuennadi Liakhovetski }; 23923bc85817SGuennadi Liakhovetski 239388d8034cSGuennadi Liakhovetski #define UVC_INFO_QUIRK(q) (kernel_ulong_t)&(struct uvc_device_info){.quirks = q} 23946ea0d588SGuennadi Liakhovetski #define UVC_INFO_META(m) (kernel_ulong_t)&(struct uvc_device_info) \ 23956ea0d588SGuennadi Liakhovetski {.meta_format = m} 23963bc85817SGuennadi Liakhovetski 23970c0d06caSMauro Carvalho Chehab /* 23980c0d06caSMauro Carvalho Chehab * The Logitech cameras listed below have their interface class set to 23990c0d06caSMauro Carvalho Chehab * VENDOR_SPEC because they don't announce themselves as UVC devices, even 24000c0d06caSMauro Carvalho Chehab * though they are compliant. 24010c0d06caSMauro Carvalho Chehab */ 24027fb2e072SArvind Yadav static const struct usb_device_id uvc_ids[] = { 240338207560SRicardo Ribalda /* Quanta USB2.0 HD UVC Webcam */ 240438207560SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 240538207560SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 240638207560SRicardo Ribalda .idVendor = 0x0408, 240738207560SRicardo Ribalda .idProduct = 0x3090, 240838207560SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 240938207560SRicardo Ribalda .bInterfaceSubClass = 1, 241038207560SRicardo Ribalda .bInterfaceProtocol = 0, 241138207560SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 241295f03d97SRicardo Ribalda /* Quanta USB2.0 HD UVC Webcam */ 241395f03d97SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 241495f03d97SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 241595f03d97SRicardo Ribalda .idVendor = 0x0408, 241695f03d97SRicardo Ribalda .idProduct = 0x4030, 241795f03d97SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 241895f03d97SRicardo Ribalda .bInterfaceSubClass = 1, 241995f03d97SRicardo Ribalda .bInterfaceProtocol = 0, 242095f03d97SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 242195f03d97SRicardo Ribalda /* Quanta USB2.0 HD UVC Webcam */ 242295f03d97SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 242395f03d97SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 242495f03d97SRicardo Ribalda .idVendor = 0x0408, 242595f03d97SRicardo Ribalda .idProduct = 0x4034, 242695f03d97SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 242795f03d97SRicardo Ribalda .bInterfaceSubClass = 1, 2428150f7b11SRicardo Ribalda .bInterfaceProtocol = UVC_PC_PROTOCOL_15, 242995f03d97SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 24300c0d06caSMauro Carvalho Chehab /* LogiLink Wireless Webcam */ 24310c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24320c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24330c0d06caSMauro Carvalho Chehab .idVendor = 0x0416, 24340c0d06caSMauro Carvalho Chehab .idProduct = 0xa91a, 24350c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 24360c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24370c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 24383bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 24390c0d06caSMauro Carvalho Chehab /* Genius eFace 2025 */ 24400c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24410c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24420c0d06caSMauro Carvalho Chehab .idVendor = 0x0458, 24430c0d06caSMauro Carvalho Chehab .idProduct = 0x706e, 24440c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 24450c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24460c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 24473bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 24480c0d06caSMauro Carvalho Chehab /* Microsoft Lifecam NX-6000 */ 24490c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24500c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24510c0d06caSMauro Carvalho Chehab .idVendor = 0x045e, 24520c0d06caSMauro Carvalho Chehab .idProduct = 0x00f8, 24530c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 24540c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24550c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 24563bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 24571558ec83SLaurent Pinchart /* Microsoft Lifecam NX-3000 */ 24581558ec83SLaurent Pinchart { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24591558ec83SLaurent Pinchart | USB_DEVICE_ID_MATCH_INT_INFO, 24601558ec83SLaurent Pinchart .idVendor = 0x045e, 24611558ec83SLaurent Pinchart .idProduct = 0x0721, 24621558ec83SLaurent Pinchart .bInterfaceClass = USB_CLASS_VIDEO, 24631558ec83SLaurent Pinchart .bInterfaceSubClass = 1, 24641558ec83SLaurent Pinchart .bInterfaceProtocol = 0, 24653bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 24660c0d06caSMauro Carvalho Chehab /* Microsoft Lifecam VX-7000 */ 24670c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24680c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24690c0d06caSMauro Carvalho Chehab .idVendor = 0x045e, 24700c0d06caSMauro Carvalho Chehab .idProduct = 0x0723, 24710c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 24720c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24730c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 24743bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 24750c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Fusion */ 24760c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24770c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24780c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 24790c0d06caSMauro Carvalho Chehab .idProduct = 0x08c1, 24800c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 24810c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24820c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 24830c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Orbit MP */ 24840c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24850c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24860c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 24870c0d06caSMauro Carvalho Chehab .idProduct = 0x08c2, 24880c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 24890c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24900c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 24910c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Pro for Notebook */ 24920c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 24930c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 24940c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 24950c0d06caSMauro Carvalho Chehab .idProduct = 0x08c3, 24960c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 24970c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 24980c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 24990c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Pro 5000 */ 25000c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25010c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 25020c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 25030c0d06caSMauro Carvalho Chehab .idProduct = 0x08c5, 25040c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 25050c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 25060c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 25070c0d06caSMauro Carvalho Chehab /* Logitech Quickcam OEM Dell Notebook */ 25080c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25090c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 25100c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 25110c0d06caSMauro Carvalho Chehab .idProduct = 0x08c6, 25120c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 25130c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 25140c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 25150c0d06caSMauro Carvalho Chehab /* Logitech Quickcam OEM Cisco VT Camera II */ 25160c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25170c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 25180c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 25190c0d06caSMauro Carvalho Chehab .idProduct = 0x08c7, 25200c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 25210c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 25220c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 252317e1319fSWilliam Manley /* Logitech HD Pro Webcam C920 */ 252417e1319fSWilliam Manley { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 252517e1319fSWilliam Manley | USB_DEVICE_ID_MATCH_INT_INFO, 252617e1319fSWilliam Manley .idVendor = 0x046d, 252717e1319fSWilliam Manley .idProduct = 0x082d, 252817e1319fSWilliam Manley .bInterfaceClass = USB_CLASS_VIDEO, 252917e1319fSWilliam Manley .bInterfaceSubClass = 1, 253017e1319fSWilliam Manley .bInterfaceProtocol = 0, 253188d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT) }, 25320c0d06caSMauro Carvalho Chehab /* Chicony CNF7129 (Asus EEE 100HE) */ 25330c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25340c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 25350c0d06caSMauro Carvalho Chehab .idVendor = 0x04f2, 25360c0d06caSMauro Carvalho Chehab .idProduct = 0xb071, 25370c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 25380c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 25390c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 254088d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTRICT_FRAME_RATE) }, 25419f22f959SRicardo Ribalda /* Chicony EasyCamera */ 25429f22f959SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25439f22f959SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 25449f22f959SRicardo Ribalda .idVendor = 0x04f2, 2545332a2235SRicardo Ribalda .idProduct = 0xb5eb, 2546332a2235SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 2547332a2235SRicardo Ribalda .bInterfaceSubClass = 1, 2548332a2235SRicardo Ribalda .bInterfaceProtocol = 0, 2549332a2235SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 2550332a2235SRicardo Ribalda /* Chicony EasyCamera */ 2551332a2235SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2552332a2235SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 2553332a2235SRicardo Ribalda .idVendor = 0x04f2, 25549f22f959SRicardo Ribalda .idProduct = 0xb6ba, 25559f22f959SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 25569f22f959SRicardo Ribalda .bInterfaceSubClass = 1, 25579f22f959SRicardo Ribalda .bInterfaceProtocol = 0, 25589f22f959SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 25599f22f959SRicardo Ribalda /* Chicony EasyCamera */ 25609f22f959SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25619f22f959SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 25629f22f959SRicardo Ribalda .idVendor = 0x04f2, 25639f22f959SRicardo Ribalda .idProduct = 0xb746, 25649f22f959SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 25659f22f959SRicardo Ribalda .bInterfaceSubClass = 1, 25669f22f959SRicardo Ribalda .bInterfaceProtocol = 0, 25679f22f959SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 25680c0d06caSMauro Carvalho Chehab /* Alcor Micro AU3820 (Future Boy PC USB Webcam) */ 25690c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25700c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 25710c0d06caSMauro Carvalho Chehab .idVendor = 0x058f, 25720c0d06caSMauro Carvalho Chehab .idProduct = 0x3820, 25730c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 25740c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 25750c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 25763bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 25770c0d06caSMauro Carvalho Chehab /* Dell XPS m1530 */ 25780c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25790c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 25800c0d06caSMauro Carvalho Chehab .idVendor = 0x05a9, 25810c0d06caSMauro Carvalho Chehab .idProduct = 0x2640, 25820c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 25830c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 25840c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 25853bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 258689e0f248SJoseph Salisbury /* Dell SP2008WFP Monitor */ 258789e0f248SJoseph Salisbury { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 258889e0f248SJoseph Salisbury | USB_DEVICE_ID_MATCH_INT_INFO, 258989e0f248SJoseph Salisbury .idVendor = 0x05a9, 259089e0f248SJoseph Salisbury .idProduct = 0x2641, 259189e0f248SJoseph Salisbury .bInterfaceClass = USB_CLASS_VIDEO, 259289e0f248SJoseph Salisbury .bInterfaceSubClass = 1, 259389e0f248SJoseph Salisbury .bInterfaceProtocol = 0, 25943bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 2595c2a273b2SJoseph Salisbury /* Dell Alienware X51 */ 2596c2a273b2SJoseph Salisbury { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2597c2a273b2SJoseph Salisbury | USB_DEVICE_ID_MATCH_INT_INFO, 2598c2a273b2SJoseph Salisbury .idVendor = 0x05a9, 2599c2a273b2SJoseph Salisbury .idProduct = 0x2643, 2600c2a273b2SJoseph Salisbury .bInterfaceClass = USB_CLASS_VIDEO, 2601c2a273b2SJoseph Salisbury .bInterfaceSubClass = 1, 2602c2a273b2SJoseph Salisbury .bInterfaceProtocol = 0, 26033bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 2604afcf44c7SKamal Mostafa /* Dell Studio Hybrid 140g (OmniVision webcam) */ 2605afcf44c7SKamal Mostafa { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2606afcf44c7SKamal Mostafa | USB_DEVICE_ID_MATCH_INT_INFO, 2607afcf44c7SKamal Mostafa .idVendor = 0x05a9, 2608afcf44c7SKamal Mostafa .idProduct = 0x264a, 2609afcf44c7SKamal Mostafa .bInterfaceClass = USB_CLASS_VIDEO, 2610afcf44c7SKamal Mostafa .bInterfaceSubClass = 1, 2611afcf44c7SKamal Mostafa .bInterfaceProtocol = 0, 26123bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 261362ea864fSPaul Fertser /* Dell XPS M1330 (OmniVision OV7670 webcam) */ 261462ea864fSPaul Fertser { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 261562ea864fSPaul Fertser | USB_DEVICE_ID_MATCH_INT_INFO, 261662ea864fSPaul Fertser .idVendor = 0x05a9, 261762ea864fSPaul Fertser .idProduct = 0x7670, 261862ea864fSPaul Fertser .bInterfaceClass = USB_CLASS_VIDEO, 261962ea864fSPaul Fertser .bInterfaceSubClass = 1, 262062ea864fSPaul Fertser .bInterfaceProtocol = 0, 26213bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 26220c0d06caSMauro Carvalho Chehab /* Apple Built-In iSight */ 26230c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26240c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26250c0d06caSMauro Carvalho Chehab .idVendor = 0x05ac, 26260c0d06caSMauro Carvalho Chehab .idProduct = 0x8501, 26270c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26280c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26290c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 263088d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 26313bc85817SGuennadi Liakhovetski | UVC_QUIRK_BUILTIN_ISIGHT) }, 263253c26454SPaul Pawlowski /* Apple FaceTime HD Camera (Built-In) */ 263353c26454SPaul Pawlowski { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 263453c26454SPaul Pawlowski | USB_DEVICE_ID_MATCH_INT_INFO, 263553c26454SPaul Pawlowski .idVendor = 0x05ac, 263653c26454SPaul Pawlowski .idProduct = 0x8514, 263753c26454SPaul Pawlowski .bInterfaceClass = USB_CLASS_VIDEO, 263853c26454SPaul Pawlowski .bInterfaceSubClass = 1, 263953c26454SPaul Pawlowski .bInterfaceProtocol = 0, 264053c26454SPaul Pawlowski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 26417b848ed6SDaniel Roschka /* Apple Built-In iSight via iBridge */ 26427b848ed6SDaniel Roschka { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26437b848ed6SDaniel Roschka | USB_DEVICE_ID_MATCH_INT_INFO, 26447b848ed6SDaniel Roschka .idVendor = 0x05ac, 26457b848ed6SDaniel Roschka .idProduct = 0x8600, 26467b848ed6SDaniel Roschka .bInterfaceClass = USB_CLASS_VIDEO, 26477b848ed6SDaniel Roschka .bInterfaceSubClass = 1, 26487b848ed6SDaniel Roschka .bInterfaceProtocol = 0, 26493bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 26500c0d06caSMauro Carvalho Chehab /* Foxlink ("HP Webcam" on HP Mini 5103) */ 26510c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26520c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26530c0d06caSMauro Carvalho Chehab .idVendor = 0x05c8, 26540c0d06caSMauro Carvalho Chehab .idProduct = 0x0403, 26550c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26560c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26570c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 26583bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 26590c0d06caSMauro Carvalho Chehab /* Genesys Logic USB 2.0 PC Camera */ 26600c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26610c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26620c0d06caSMauro Carvalho Chehab .idVendor = 0x05e3, 26630c0d06caSMauro Carvalho Chehab .idProduct = 0x0505, 26640c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26650c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26660c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 26673bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 26680c0d06caSMauro Carvalho Chehab /* Hercules Classic Silver */ 26690c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26700c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26710c0d06caSMauro Carvalho Chehab .idVendor = 0x06f8, 26720c0d06caSMauro Carvalho Chehab .idProduct = 0x300c, 26730c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26740c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26750c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 26763bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 26770c0d06caSMauro Carvalho Chehab /* ViMicro Vega */ 26780c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26790c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26800c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 26810c0d06caSMauro Carvalho Chehab .idProduct = 0x332d, 26820c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26830c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26840c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 26853bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 26860c0d06caSMauro Carvalho Chehab /* ViMicro - Minoru3D */ 26870c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26880c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26890c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 26900c0d06caSMauro Carvalho Chehab .idProduct = 0x3410, 26910c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26920c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26930c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 26943bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 26950c0d06caSMauro Carvalho Chehab /* ViMicro Venus - Minoru3D */ 26960c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26970c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26980c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 26990c0d06caSMauro Carvalho Chehab .idProduct = 0x3420, 27000c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27010c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27020c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27033bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 27040c0d06caSMauro Carvalho Chehab /* Ophir Optronics - SPCAM 620U */ 27050c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27060c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27070c0d06caSMauro Carvalho Chehab .idVendor = 0x0bd3, 27080c0d06caSMauro Carvalho Chehab .idProduct = 0x0555, 27090c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27100c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27110c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27123bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 27130c0d06caSMauro Carvalho Chehab /* MT6227 */ 27140c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27150c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27160c0d06caSMauro Carvalho Chehab .idVendor = 0x0e8d, 27170c0d06caSMauro Carvalho Chehab .idProduct = 0x0004, 27180c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27190c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27200c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 272188d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 27223bc85817SGuennadi Liakhovetski | UVC_QUIRK_PROBE_DEF) }, 27230c0d06caSMauro Carvalho Chehab /* IMC Networks (Medion Akoya) */ 27240c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27250c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27260c0d06caSMauro Carvalho Chehab .idVendor = 0x13d3, 27270c0d06caSMauro Carvalho Chehab .idProduct = 0x5103, 27280c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27290c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27300c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27313bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 27320c0d06caSMauro Carvalho Chehab /* JMicron USB2.0 XGA WebCam */ 27330c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27340c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27350c0d06caSMauro Carvalho Chehab .idVendor = 0x152d, 27360c0d06caSMauro Carvalho Chehab .idProduct = 0x0310, 27370c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27380c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27390c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27403bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 27410c0d06caSMauro Carvalho Chehab /* Syntek (HP Spartan) */ 27420c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27430c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27440c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 27450c0d06caSMauro Carvalho Chehab .idProduct = 0x5212, 27460c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27470c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27480c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27493bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 27500c0d06caSMauro Carvalho Chehab /* Syntek (Samsung Q310) */ 27510c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27520c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27530c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 27540c0d06caSMauro Carvalho Chehab .idProduct = 0x5931, 27550c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27560c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27570c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27583bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 27590c0d06caSMauro Carvalho Chehab /* Syntek (Packard Bell EasyNote MX52 */ 27600c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27610c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27620c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 27630c0d06caSMauro Carvalho Chehab .idProduct = 0x8a12, 27640c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27650c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27660c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27673bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 27680c0d06caSMauro Carvalho Chehab /* Syntek (Asus F9SG) */ 27690c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27700c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27710c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 27720c0d06caSMauro Carvalho Chehab .idProduct = 0x8a31, 27730c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27740c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27750c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27763bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 27770c0d06caSMauro Carvalho Chehab /* Syntek (Asus U3S) */ 27780c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27790c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27800c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 27810c0d06caSMauro Carvalho Chehab .idProduct = 0x8a33, 27820c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27830c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27840c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27853bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 27860c0d06caSMauro Carvalho Chehab /* Syntek (JAOtech Smart Terminal) */ 27870c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27880c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27890c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 27900c0d06caSMauro Carvalho Chehab .idProduct = 0x8a34, 27910c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27920c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27930c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27943bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 27950c0d06caSMauro Carvalho Chehab /* Miricle 307K */ 27960c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27970c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27980c0d06caSMauro Carvalho Chehab .idVendor = 0x17dc, 27990c0d06caSMauro Carvalho Chehab .idProduct = 0x0202, 28000c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28010c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28020c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 28033bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 28040c0d06caSMauro Carvalho Chehab /* Lenovo Thinkpad SL400/SL500 */ 28050c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28060c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28070c0d06caSMauro Carvalho Chehab .idVendor = 0x17ef, 28080c0d06caSMauro Carvalho Chehab .idProduct = 0x480b, 28090c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28100c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28110c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 28123bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 28130c0d06caSMauro Carvalho Chehab /* Aveo Technology USB 2.0 Camera */ 28140c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28150c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28160c0d06caSMauro Carvalho Chehab .idVendor = 0x1871, 28170c0d06caSMauro Carvalho Chehab .idProduct = 0x0306, 28180c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28190c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28200c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 282188d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 28223bc85817SGuennadi Liakhovetski | UVC_QUIRK_PROBE_EXTRAFIELDS) }, 2823fe652471SLaurent Pinchart /* Aveo Technology USB 2.0 Camera (Tasco USB Microscope) */ 2824fe652471SLaurent Pinchart { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2825fe652471SLaurent Pinchart | USB_DEVICE_ID_MATCH_INT_INFO, 2826fe652471SLaurent Pinchart .idVendor = 0x1871, 2827fe652471SLaurent Pinchart .idProduct = 0x0516, 2828fe652471SLaurent Pinchart .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 2829fe652471SLaurent Pinchart .bInterfaceSubClass = 1, 2830fe652471SLaurent Pinchart .bInterfaceProtocol = 0 }, 28310c0d06caSMauro Carvalho Chehab /* Ecamm Pico iMage */ 28320c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28330c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28340c0d06caSMauro Carvalho Chehab .idVendor = 0x18cd, 28350c0d06caSMauro Carvalho Chehab .idProduct = 0xcafe, 28360c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28370c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28380c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 283988d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_EXTRAFIELDS) }, 28400c0d06caSMauro Carvalho Chehab /* Manta MM-353 Plako */ 28410c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28420c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28430c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 28440c0d06caSMauro Carvalho Chehab .idProduct = 0x3188, 28450c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28460c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28470c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 28483bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 28490c0d06caSMauro Carvalho Chehab /* FSC WebCam V30S */ 28500c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28510c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28520c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 28530c0d06caSMauro Carvalho Chehab .idProduct = 0x3288, 28540c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28550c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28560c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 28573bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 28580c0d06caSMauro Carvalho Chehab /* Arkmicro unbranded */ 28590c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28600c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28610c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 28620c0d06caSMauro Carvalho Chehab .idProduct = 0x3290, 28630c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28640c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28650c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 28663bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 28670c0d06caSMauro Carvalho Chehab /* The Imaging Source USB CCD cameras */ 28680c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28690c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28700c0d06caSMauro Carvalho Chehab .idVendor = 0x199e, 28710c0d06caSMauro Carvalho Chehab .idProduct = 0x8102, 28720c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 28730c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28740c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 28750c0d06caSMauro Carvalho Chehab /* Bodelin ProScopeHR */ 28760c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28770c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_DEV_HI 28780c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28790c0d06caSMauro Carvalho Chehab .idVendor = 0x19ab, 28800c0d06caSMauro Carvalho Chehab .idProduct = 0x1000, 28810c0d06caSMauro Carvalho Chehab .bcdDevice_hi = 0x0126, 28820c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28830c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28840c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 288588d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_STATUS_INTERVAL) }, 28860c0d06caSMauro Carvalho Chehab /* MSI StarCam 370i */ 28870c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28880c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28890c0d06caSMauro Carvalho Chehab .idVendor = 0x1b3b, 28900c0d06caSMauro Carvalho Chehab .idProduct = 0x2951, 28910c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28920c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28930c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 28943bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 2895589266bdSNeil Armstrong /* Generalplus Technology Inc. 808 Camera */ 2896589266bdSNeil Armstrong { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2897589266bdSNeil Armstrong | USB_DEVICE_ID_MATCH_INT_INFO, 2898589266bdSNeil Armstrong .idVendor = 0x1b3f, 2899589266bdSNeil Armstrong .idProduct = 0x2002, 2900589266bdSNeil Armstrong .bInterfaceClass = USB_CLASS_VIDEO, 2901589266bdSNeil Armstrong .bInterfaceSubClass = 1, 2902589266bdSNeil Armstrong .bInterfaceProtocol = 0, 2903589266bdSNeil Armstrong .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 2904b400b6f2SLaurent Pinchart /* Shenzhen Aoni Electronic Co.,Ltd 2K FHD camera */ 2905b400b6f2SLaurent Pinchart { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2906b400b6f2SLaurent Pinchart | USB_DEVICE_ID_MATCH_INT_INFO, 2907b400b6f2SLaurent Pinchart .idVendor = 0x1bcf, 2908b400b6f2SLaurent Pinchart .idProduct = 0x0b40, 2909b400b6f2SLaurent Pinchart .bInterfaceClass = USB_CLASS_VIDEO, 2910b400b6f2SLaurent Pinchart .bInterfaceSubClass = 1, 2911b400b6f2SLaurent Pinchart .bInterfaceProtocol = 0, 2912b400b6f2SLaurent Pinchart .driver_info = (kernel_ulong_t)&(const struct uvc_device_info){ 2913b400b6f2SLaurent Pinchart .uvc_version = 0x010a, 2914b400b6f2SLaurent Pinchart } }, 29150c0d06caSMauro Carvalho Chehab /* SiGma Micro USB Web Camera */ 29160c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29170c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 29180c0d06caSMauro Carvalho Chehab .idVendor = 0x1c4f, 29190c0d06caSMauro Carvalho Chehab .idProduct = 0x3000, 29200c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 29210c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 29220c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 292388d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 29243bc85817SGuennadi Liakhovetski | UVC_QUIRK_IGNORE_SELECTOR_UNIT) }, 2925e1b78a33SPhilipp Zabel /* Oculus VR Positional Tracker DK2 */ 2926e1b78a33SPhilipp Zabel { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2927e1b78a33SPhilipp Zabel | USB_DEVICE_ID_MATCH_INT_INFO, 2928e1b78a33SPhilipp Zabel .idVendor = 0x2833, 2929e1b78a33SPhilipp Zabel .idProduct = 0x0201, 2930e1b78a33SPhilipp Zabel .bInterfaceClass = USB_CLASS_VIDEO, 2931e1b78a33SPhilipp Zabel .bInterfaceSubClass = 1, 2932e1b78a33SPhilipp Zabel .bInterfaceProtocol = 0, 29333bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_force_y8 }, 293403c47aaeSPhilipp Zabel /* Oculus VR Rift Sensor */ 293503c47aaeSPhilipp Zabel { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 293603c47aaeSPhilipp Zabel | USB_DEVICE_ID_MATCH_INT_INFO, 293703c47aaeSPhilipp Zabel .idVendor = 0x2833, 293803c47aaeSPhilipp Zabel .idProduct = 0x0211, 293903c47aaeSPhilipp Zabel .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 294003c47aaeSPhilipp Zabel .bInterfaceSubClass = 1, 294103c47aaeSPhilipp Zabel .bInterfaceProtocol = 0, 29423bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_force_y8 }, 29431dd2e8f9SSergey Zakharchenko /* GEO Semiconductor GC6500 */ 29441dd2e8f9SSergey Zakharchenko { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29451dd2e8f9SSergey Zakharchenko | USB_DEVICE_ID_MATCH_INT_INFO, 29461dd2e8f9SSergey Zakharchenko .idVendor = 0x29fe, 29471dd2e8f9SSergey Zakharchenko .idProduct = 0x4d53, 29481dd2e8f9SSergey Zakharchenko .bInterfaceClass = USB_CLASS_VIDEO, 29491dd2e8f9SSergey Zakharchenko .bInterfaceSubClass = 1, 29501dd2e8f9SSergey Zakharchenko .bInterfaceProtocol = 0, 29511dd2e8f9SSergey Zakharchenko .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_FORCE_BPP) }, 2952101418b3Shuanglei /* Sonix Technology USB 2.0 Camera */ 2953101418b3Shuanglei { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2954101418b3Shuanglei | USB_DEVICE_ID_MATCH_INT_INFO, 2955101418b3Shuanglei .idVendor = 0x3277, 2956101418b3Shuanglei .idProduct = 0x0072, 2957101418b3Shuanglei .bInterfaceClass = USB_CLASS_VIDEO, 2958101418b3Shuanglei .bInterfaceSubClass = 1, 2959101418b3Shuanglei .bInterfaceProtocol = 0, 2960101418b3Shuanglei .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 2961eff1e24cSRicardo Ribalda /* Acer EasyCamera */ 2962eff1e24cSRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2963eff1e24cSRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 2964eff1e24cSRicardo Ribalda .idVendor = 0x5986, 2965eff1e24cSRicardo Ribalda .idProduct = 0x1172, 2966eff1e24cSRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 2967eff1e24cSRicardo Ribalda .bInterfaceSubClass = 1, 2968eff1e24cSRicardo Ribalda .bInterfaceProtocol = 0, 2969eff1e24cSRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 2970*81e78a6fSRicardo Ribalda /* Acer EasyCamera */ 2971*81e78a6fSRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2972*81e78a6fSRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 2973*81e78a6fSRicardo Ribalda .idVendor = 0x5986, 2974*81e78a6fSRicardo Ribalda .idProduct = 0x1180, 2975*81e78a6fSRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 2976*81e78a6fSRicardo Ribalda .bInterfaceSubClass = 1, 2977*81e78a6fSRicardo Ribalda .bInterfaceProtocol = 0, 2978*81e78a6fSRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 29796ea0d588SGuennadi Liakhovetski /* Intel RealSense D4M */ 29806ea0d588SGuennadi Liakhovetski { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29816ea0d588SGuennadi Liakhovetski | USB_DEVICE_ID_MATCH_INT_INFO, 29826ea0d588SGuennadi Liakhovetski .idVendor = 0x8086, 29836ea0d588SGuennadi Liakhovetski .idProduct = 0x0b03, 29846ea0d588SGuennadi Liakhovetski .bInterfaceClass = USB_CLASS_VIDEO, 29856ea0d588SGuennadi Liakhovetski .bInterfaceSubClass = 1, 29866ea0d588SGuennadi Liakhovetski .bInterfaceProtocol = 0, 29876ea0d588SGuennadi Liakhovetski .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 29880c0d06caSMauro Carvalho Chehab /* Generic USB Video Class */ 29898afe97beSLaurent Pinchart { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_UNDEFINED) }, 29908afe97beSLaurent Pinchart { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_15) }, 29910c0d06caSMauro Carvalho Chehab {} 29920c0d06caSMauro Carvalho Chehab }; 29930c0d06caSMauro Carvalho Chehab 29940c0d06caSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, uvc_ids); 29950c0d06caSMauro Carvalho Chehab 29960c0d06caSMauro Carvalho Chehab struct uvc_driver uvc_driver = { 29970c0d06caSMauro Carvalho Chehab .driver = { 29980c0d06caSMauro Carvalho Chehab .name = "uvcvideo", 29990c0d06caSMauro Carvalho Chehab .probe = uvc_probe, 30000c0d06caSMauro Carvalho Chehab .disconnect = uvc_disconnect, 30010c0d06caSMauro Carvalho Chehab .suspend = uvc_suspend, 30020c0d06caSMauro Carvalho Chehab .resume = uvc_resume, 30030c0d06caSMauro Carvalho Chehab .reset_resume = uvc_reset_resume, 30040c0d06caSMauro Carvalho Chehab .id_table = uvc_ids, 30050c0d06caSMauro Carvalho Chehab .supports_autosuspend = 1, 30060c0d06caSMauro Carvalho Chehab }, 30070c0d06caSMauro Carvalho Chehab }; 30080c0d06caSMauro Carvalho Chehab 30090c0d06caSMauro Carvalho Chehab static int __init uvc_init(void) 30100c0d06caSMauro Carvalho Chehab { 30110c0d06caSMauro Carvalho Chehab int ret; 30120c0d06caSMauro Carvalho Chehab 30130c0d06caSMauro Carvalho Chehab uvc_debugfs_init(); 30140c0d06caSMauro Carvalho Chehab 30150c0d06caSMauro Carvalho Chehab ret = usb_register(&uvc_driver.driver); 30160c0d06caSMauro Carvalho Chehab if (ret < 0) { 30170c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup(); 30180c0d06caSMauro Carvalho Chehab return ret; 30190c0d06caSMauro Carvalho Chehab } 30200c0d06caSMauro Carvalho Chehab 30210c0d06caSMauro Carvalho Chehab return 0; 30220c0d06caSMauro Carvalho Chehab } 30230c0d06caSMauro Carvalho Chehab 30240c0d06caSMauro Carvalho Chehab static void __exit uvc_cleanup(void) 30250c0d06caSMauro Carvalho Chehab { 30260c0d06caSMauro Carvalho Chehab usb_deregister(&uvc_driver.driver); 30270c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup(); 30280c0d06caSMauro Carvalho Chehab } 30290c0d06caSMauro Carvalho Chehab 30300c0d06caSMauro Carvalho Chehab module_init(uvc_init); 30310c0d06caSMauro Carvalho Chehab module_exit(uvc_cleanup); 30320c0d06caSMauro Carvalho Chehab 30330c0d06caSMauro Carvalho Chehab MODULE_AUTHOR(DRIVER_AUTHOR); 30340c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION(DRIVER_DESC); 30350c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 30360c0d06caSMauro Carvalho Chehab MODULE_VERSION(DRIVER_VERSION); 30370c0d06caSMauro Carvalho Chehab 3038