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> 1040140edaSRicardo Ribalda #include <linux/bits.h> 112886477fSRicardo Ribalda #include <linux/gpio/consumer.h> 120c0d06caSMauro Carvalho Chehab #include <linux/kernel.h> 130c0d06caSMauro Carvalho Chehab #include <linux/list.h> 140c0d06caSMauro Carvalho Chehab #include <linux/module.h> 150c0d06caSMauro Carvalho Chehab #include <linux/slab.h> 160c0d06caSMauro Carvalho Chehab #include <linux/usb.h> 174b3421c2SRicardo Ribalda #include <linux/usb/quirks.h> 18e1d5d71dSMichael Grzeschik #include <linux/usb/uvc.h> 190c0d06caSMauro Carvalho Chehab #include <linux/videodev2.h> 200c0d06caSMauro Carvalho Chehab #include <linux/vmalloc.h> 210c0d06caSMauro Carvalho Chehab #include <linux/wait.h> 220c0d06caSMauro Carvalho Chehab #include <asm/unaligned.h> 230c0d06caSMauro Carvalho Chehab 240c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h> 2531a96f4cSLaurent Pinchart #include <media/v4l2-ioctl.h> 260c0d06caSMauro Carvalho Chehab 270c0d06caSMauro Carvalho Chehab #include "uvcvideo.h" 280c0d06caSMauro Carvalho Chehab 290c0d06caSMauro Carvalho Chehab #define DRIVER_AUTHOR "Laurent Pinchart " \ 300c0d06caSMauro Carvalho Chehab "<laurent.pinchart@ideasonboard.com>" 310c0d06caSMauro Carvalho Chehab #define DRIVER_DESC "USB Video Class driver" 320c0d06caSMauro Carvalho Chehab 330c0d06caSMauro Carvalho Chehab unsigned int uvc_clock_param = CLOCK_MONOTONIC; 345d0fd3c8SLaurent Pinchart unsigned int uvc_hw_timestamps_param; 350c0d06caSMauro Carvalho Chehab unsigned int uvc_no_drop_param; 360c0d06caSMauro Carvalho Chehab static unsigned int uvc_quirks_param = -1; 379e56380aSJoe Perches unsigned int uvc_dbg_param; 380c0d06caSMauro Carvalho Chehab unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; 390c0d06caSMauro Carvalho Chehab 400c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 410c0d06caSMauro Carvalho Chehab * Utility functions 420c0d06caSMauro Carvalho Chehab */ 430c0d06caSMauro Carvalho Chehab 440c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, 452c6b222cSLaurent Pinchart u8 epaddr) 460c0d06caSMauro Carvalho Chehab { 470c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep; 480c0d06caSMauro Carvalho Chehab unsigned int i; 490c0d06caSMauro Carvalho Chehab 500c0d06caSMauro Carvalho Chehab for (i = 0; i < alts->desc.bNumEndpoints; ++i) { 510c0d06caSMauro Carvalho Chehab ep = &alts->endpoint[i]; 520c0d06caSMauro Carvalho Chehab if (ep->desc.bEndpointAddress == epaddr) 530c0d06caSMauro Carvalho Chehab return ep; 540c0d06caSMauro Carvalho Chehab } 550c0d06caSMauro Carvalho Chehab 560c0d06caSMauro Carvalho Chehab return NULL; 570c0d06caSMauro Carvalho Chehab } 580c0d06caSMauro Carvalho Chehab 59ec2c23f6SAdam Goode static enum v4l2_colorspace uvc_colorspace(const u8 primaries) 600c0d06caSMauro Carvalho Chehab { 61ec2c23f6SAdam Goode static const enum v4l2_colorspace colorprimaries[] = { 62e82822faSRicardo Ribalda V4L2_COLORSPACE_SRGB, /* Unspecified */ 630c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SRGB, 640c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_470_SYSTEM_M, 650c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_470_SYSTEM_BG, 660c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SMPTE170M, 670c0d06caSMauro Carvalho Chehab V4L2_COLORSPACE_SMPTE240M, 680c0d06caSMauro Carvalho Chehab }; 690c0d06caSMauro Carvalho Chehab 700c0d06caSMauro Carvalho Chehab if (primaries < ARRAY_SIZE(colorprimaries)) 710c0d06caSMauro Carvalho Chehab return colorprimaries[primaries]; 720c0d06caSMauro Carvalho Chehab 73e82822faSRicardo Ribalda return V4L2_COLORSPACE_SRGB; /* Reserved */ 74ec2c23f6SAdam Goode } 75ec2c23f6SAdam Goode 76ec2c23f6SAdam Goode static enum v4l2_xfer_func uvc_xfer_func(const u8 transfer_characteristics) 77ec2c23f6SAdam Goode { 78ec2c23f6SAdam Goode /* 79ec2c23f6SAdam Goode * V4L2 does not currently have definitions for all possible values of 80ec2c23f6SAdam Goode * UVC transfer characteristics. If v4l2_xfer_func is extended with new 81ec2c23f6SAdam Goode * values, the mapping below should be updated. 82ec2c23f6SAdam Goode * 83ec2c23f6SAdam Goode * Substitutions are taken from the mapping given for 84ec2c23f6SAdam Goode * V4L2_XFER_FUNC_DEFAULT documented in videodev2.h. 85ec2c23f6SAdam Goode */ 86ec2c23f6SAdam Goode static const enum v4l2_xfer_func xfer_funcs[] = { 87ec2c23f6SAdam Goode V4L2_XFER_FUNC_DEFAULT, /* Unspecified */ 88ec2c23f6SAdam Goode V4L2_XFER_FUNC_709, 89ec2c23f6SAdam Goode V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 M */ 90ec2c23f6SAdam Goode V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 B, G */ 91ec2c23f6SAdam Goode V4L2_XFER_FUNC_709, /* Substitution for SMPTE 170M */ 92ec2c23f6SAdam Goode V4L2_XFER_FUNC_SMPTE240M, 93ec2c23f6SAdam Goode V4L2_XFER_FUNC_NONE, 94ec2c23f6SAdam Goode V4L2_XFER_FUNC_SRGB, 95ec2c23f6SAdam Goode }; 96ec2c23f6SAdam Goode 97ec2c23f6SAdam Goode if (transfer_characteristics < ARRAY_SIZE(xfer_funcs)) 98ec2c23f6SAdam Goode return xfer_funcs[transfer_characteristics]; 99ec2c23f6SAdam Goode 100ec2c23f6SAdam Goode return V4L2_XFER_FUNC_DEFAULT; /* Reserved */ 101ec2c23f6SAdam Goode } 102ec2c23f6SAdam Goode 103ec2c23f6SAdam Goode static enum v4l2_ycbcr_encoding uvc_ycbcr_enc(const u8 matrix_coefficients) 104ec2c23f6SAdam Goode { 105ec2c23f6SAdam Goode /* 106ec2c23f6SAdam Goode * V4L2 does not currently have definitions for all possible values of 107ec2c23f6SAdam Goode * UVC matrix coefficients. If v4l2_ycbcr_encoding is extended with new 108ec2c23f6SAdam Goode * values, the mapping below should be updated. 109ec2c23f6SAdam Goode * 110ec2c23f6SAdam Goode * Substitutions are taken from the mapping given for 111ec2c23f6SAdam Goode * V4L2_YCBCR_ENC_DEFAULT documented in videodev2.h. 112ec2c23f6SAdam Goode * 113ec2c23f6SAdam Goode * FCC is assumed to be close enough to 601. 114ec2c23f6SAdam Goode */ 115ec2c23f6SAdam Goode static const enum v4l2_ycbcr_encoding ycbcr_encs[] = { 116ec2c23f6SAdam Goode V4L2_YCBCR_ENC_DEFAULT, /* Unspecified */ 117ec2c23f6SAdam Goode V4L2_YCBCR_ENC_709, 118ec2c23f6SAdam Goode V4L2_YCBCR_ENC_601, /* Substitution for FCC */ 119ec2c23f6SAdam Goode V4L2_YCBCR_ENC_601, /* Substitution for BT.470-2 B, G */ 120ec2c23f6SAdam Goode V4L2_YCBCR_ENC_601, 121ec2c23f6SAdam Goode V4L2_YCBCR_ENC_SMPTE240M, 122ec2c23f6SAdam Goode }; 123ec2c23f6SAdam Goode 124ec2c23f6SAdam Goode if (matrix_coefficients < ARRAY_SIZE(ycbcr_encs)) 125ec2c23f6SAdam Goode return ycbcr_encs[matrix_coefficients]; 126ec2c23f6SAdam Goode 127ec2c23f6SAdam Goode return V4L2_YCBCR_ENC_DEFAULT; /* Reserved */ 1280c0d06caSMauro Carvalho Chehab } 1290c0d06caSMauro Carvalho Chehab 1300c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 1310c0d06caSMauro Carvalho Chehab * Terminal and unit management 1320c0d06caSMauro Carvalho Chehab */ 1330c0d06caSMauro Carvalho Chehab 1340c0d06caSMauro Carvalho Chehab struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id) 1350c0d06caSMauro Carvalho Chehab { 1360c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 1370c0d06caSMauro Carvalho Chehab 1380c0d06caSMauro Carvalho Chehab list_for_each_entry(entity, &dev->entities, list) { 1390c0d06caSMauro Carvalho Chehab if (entity->id == id) 1400c0d06caSMauro Carvalho Chehab return entity; 1410c0d06caSMauro Carvalho Chehab } 1420c0d06caSMauro Carvalho Chehab 1430c0d06caSMauro Carvalho Chehab return NULL; 1440c0d06caSMauro Carvalho Chehab } 1450c0d06caSMauro Carvalho Chehab 1460c0d06caSMauro Carvalho Chehab static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, 1470c0d06caSMauro Carvalho Chehab int id, struct uvc_entity *entity) 1480c0d06caSMauro Carvalho Chehab { 1490c0d06caSMauro Carvalho Chehab unsigned int i; 1500c0d06caSMauro Carvalho Chehab 1510c0d06caSMauro Carvalho Chehab if (entity == NULL) 1520c0d06caSMauro Carvalho Chehab entity = list_entry(&dev->entities, struct uvc_entity, list); 1530c0d06caSMauro Carvalho Chehab 1540c0d06caSMauro Carvalho Chehab list_for_each_entry_continue(entity, &dev->entities, list) { 1550c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->bNrInPins; ++i) 1560c0d06caSMauro Carvalho Chehab if (entity->baSourceID[i] == id) 1570c0d06caSMauro Carvalho Chehab return entity; 1580c0d06caSMauro Carvalho Chehab } 1590c0d06caSMauro Carvalho Chehab 1600c0d06caSMauro Carvalho Chehab return NULL; 1610c0d06caSMauro Carvalho Chehab } 1620c0d06caSMauro Carvalho Chehab 1630c0d06caSMauro Carvalho Chehab static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id) 1640c0d06caSMauro Carvalho Chehab { 1650c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 1660c0d06caSMauro Carvalho Chehab 1670c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 1680c0d06caSMauro Carvalho Chehab if (stream->header.bTerminalLink == id) 1690c0d06caSMauro Carvalho Chehab return stream; 1700c0d06caSMauro Carvalho Chehab } 1710c0d06caSMauro Carvalho Chehab 1720c0d06caSMauro Carvalho Chehab return NULL; 1730c0d06caSMauro Carvalho Chehab } 1740c0d06caSMauro Carvalho Chehab 1750c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 176ece41454SKieran Bingham * Streaming Object Management 177ece41454SKieran Bingham */ 178ece41454SKieran Bingham 179ece41454SKieran Bingham static void uvc_stream_delete(struct uvc_streaming *stream) 180ece41454SKieran Bingham { 181b012186aSKieran Bingham if (stream->async_wq) 182b012186aSKieran Bingham destroy_workqueue(stream->async_wq); 183b012186aSKieran Bingham 184ece41454SKieran Bingham mutex_destroy(&stream->mutex); 185ece41454SKieran Bingham 186ece41454SKieran Bingham usb_put_intf(stream->intf); 187ece41454SKieran Bingham 188ccfad4e8SLaurent Pinchart kfree(stream->formats); 189ece41454SKieran Bingham kfree(stream->header.bmaControls); 190ece41454SKieran Bingham kfree(stream); 191ece41454SKieran Bingham } 192ece41454SKieran Bingham 193ece41454SKieran Bingham static struct uvc_streaming *uvc_stream_new(struct uvc_device *dev, 194ece41454SKieran Bingham struct usb_interface *intf) 195ece41454SKieran Bingham { 196ece41454SKieran Bingham struct uvc_streaming *stream; 197ece41454SKieran Bingham 198ece41454SKieran Bingham stream = kzalloc(sizeof(*stream), GFP_KERNEL); 199ece41454SKieran Bingham if (stream == NULL) 200ece41454SKieran Bingham return NULL; 201ece41454SKieran Bingham 202ece41454SKieran Bingham mutex_init(&stream->mutex); 203ece41454SKieran Bingham 204ece41454SKieran Bingham stream->dev = dev; 205ece41454SKieran Bingham stream->intf = usb_get_intf(intf); 206ece41454SKieran Bingham stream->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; 207ece41454SKieran Bingham 208b012186aSKieran Bingham /* Allocate a stream specific work queue for asynchronous tasks. */ 209b012186aSKieran Bingham stream->async_wq = alloc_workqueue("uvcvideo", WQ_UNBOUND | WQ_HIGHPRI, 210b012186aSKieran Bingham 0); 211b012186aSKieran Bingham if (!stream->async_wq) { 212b012186aSKieran Bingham uvc_stream_delete(stream); 213b012186aSKieran Bingham return NULL; 214b012186aSKieran Bingham } 215b012186aSKieran Bingham 216ece41454SKieran Bingham return stream; 217ece41454SKieran Bingham } 218ece41454SKieran Bingham 219ece41454SKieran Bingham /* ------------------------------------------------------------------------ 2200c0d06caSMauro Carvalho Chehab * Descriptors parsing 2210c0d06caSMauro Carvalho Chehab */ 2220c0d06caSMauro Carvalho Chehab 2230c0d06caSMauro Carvalho Chehab static int uvc_parse_format(struct uvc_device *dev, 2240c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming, struct uvc_format *format, 225102df33eSLaurent Pinchart struct uvc_frame *frames, u32 **intervals, const unsigned char *buffer, 226af621ba2SLaurent Pinchart int buflen) 2270c0d06caSMauro Carvalho Chehab { 2280c0d06caSMauro Carvalho Chehab struct usb_interface *intf = streaming->intf; 2290c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = intf->cur_altsetting; 2308ecb17a8SMichael Grzeschik const struct uvc_format_desc *fmtdesc; 2310c0d06caSMauro Carvalho Chehab struct uvc_frame *frame; 2320c0d06caSMauro Carvalho Chehab const unsigned char *start = buffer; 233e1b78a33SPhilipp Zabel unsigned int width_multiplier = 1; 2340c0d06caSMauro Carvalho Chehab unsigned int interval; 2350c0d06caSMauro Carvalho Chehab unsigned int i, n; 2362c6b222cSLaurent Pinchart u8 ftype; 2370c0d06caSMauro Carvalho Chehab 2380c0d06caSMauro Carvalho Chehab format->type = buffer[2]; 2390c0d06caSMauro Carvalho Chehab format->index = buffer[3]; 240af621ba2SLaurent Pinchart format->frames = frames; 2410c0d06caSMauro Carvalho Chehab 2420c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 2430c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 2440c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 2450c0d06caSMauro Carvalho Chehab n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28; 2460c0d06caSMauro Carvalho Chehab if (buflen < n) { 2479e56380aSJoe Perches uvc_dbg(dev, DESCR, 248ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d FORMAT error\n", 2490c0d06caSMauro Carvalho Chehab dev->udev->devnum, 2500c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 2510c0d06caSMauro Carvalho Chehab return -EINVAL; 2520c0d06caSMauro Carvalho Chehab } 2530c0d06caSMauro Carvalho Chehab 2540c0d06caSMauro Carvalho Chehab /* Find the format descriptor from its GUID. */ 2550c0d06caSMauro Carvalho Chehab fmtdesc = uvc_format_by_guid(&buffer[5]); 2560c0d06caSMauro Carvalho Chehab 25781f3affaSLaurent Pinchart if (!fmtdesc) { 25881f3affaSLaurent Pinchart /* 25981f3affaSLaurent Pinchart * Unknown video formats are not fatal errors, the 26081f3affaSLaurent Pinchart * caller will skip this descriptor. 26181f3affaSLaurent Pinchart */ 26269df0954SRicardo Ribalda dev_info(&streaming->intf->dev, 26369df0954SRicardo Ribalda "Unknown video format %pUl\n", &buffer[5]); 26481f3affaSLaurent Pinchart return 0; 2650c0d06caSMauro Carvalho Chehab } 2660c0d06caSMauro Carvalho Chehab 26781f3affaSLaurent Pinchart format->fcc = fmtdesc->fcc; 2680c0d06caSMauro Carvalho Chehab format->bpp = buffer[21]; 269e1b78a33SPhilipp Zabel 270699b9a86SLaurent Pinchart /* 271699b9a86SLaurent Pinchart * Some devices report a format that doesn't match what they 272e1b78a33SPhilipp Zabel * really send. 273e1b78a33SPhilipp Zabel */ 274e1b78a33SPhilipp Zabel if (dev->quirks & UVC_QUIRK_FORCE_Y8) { 275e1b78a33SPhilipp Zabel if (format->fcc == V4L2_PIX_FMT_YUYV) { 276e1b78a33SPhilipp Zabel format->fcc = V4L2_PIX_FMT_GREY; 277e1b78a33SPhilipp Zabel format->bpp = 8; 278e1b78a33SPhilipp Zabel width_multiplier = 2; 279e1b78a33SPhilipp Zabel } 280e1b78a33SPhilipp Zabel } 281e1b78a33SPhilipp Zabel 2821dd2e8f9SSergey Zakharchenko /* Some devices report bpp that doesn't match the format. */ 2831dd2e8f9SSergey Zakharchenko if (dev->quirks & UVC_QUIRK_FORCE_BPP) { 2841dd2e8f9SSergey Zakharchenko const struct v4l2_format_info *info = 2851dd2e8f9SSergey Zakharchenko v4l2_format_info(format->fcc); 2861dd2e8f9SSergey Zakharchenko 2871dd2e8f9SSergey Zakharchenko if (info) { 2881dd2e8f9SSergey Zakharchenko unsigned int div = info->hdiv * info->vdiv; 2891dd2e8f9SSergey Zakharchenko 2901dd2e8f9SSergey Zakharchenko n = info->bpp[0] * div; 2911dd2e8f9SSergey Zakharchenko for (i = 1; i < info->comp_planes; i++) 2921dd2e8f9SSergey Zakharchenko n += info->bpp[i]; 2931dd2e8f9SSergey Zakharchenko 2941dd2e8f9SSergey Zakharchenko format->bpp = DIV_ROUND_UP(8 * n, div); 2951dd2e8f9SSergey Zakharchenko } 2961dd2e8f9SSergey Zakharchenko } 2971dd2e8f9SSergey Zakharchenko 2980c0d06caSMauro Carvalho Chehab if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) { 2990c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_UNCOMPRESSED; 3000c0d06caSMauro Carvalho Chehab } else { 3010c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_FRAME_BASED; 3020c0d06caSMauro Carvalho Chehab if (buffer[27]) 3030c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED; 3040c0d06caSMauro Carvalho Chehab } 3050c0d06caSMauro Carvalho Chehab break; 3060c0d06caSMauro Carvalho Chehab 3070c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 3080c0d06caSMauro Carvalho Chehab if (buflen < 11) { 3099e56380aSJoe Perches uvc_dbg(dev, DESCR, 310ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d FORMAT error\n", 3110c0d06caSMauro Carvalho Chehab dev->udev->devnum, 3120c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 3130c0d06caSMauro Carvalho Chehab return -EINVAL; 3140c0d06caSMauro Carvalho Chehab } 3150c0d06caSMauro Carvalho Chehab 3160c0d06caSMauro Carvalho Chehab format->fcc = V4L2_PIX_FMT_MJPEG; 3170c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED; 3180c0d06caSMauro Carvalho Chehab format->bpp = 0; 3190c0d06caSMauro Carvalho Chehab ftype = UVC_VS_FRAME_MJPEG; 3200c0d06caSMauro Carvalho Chehab break; 3210c0d06caSMauro Carvalho Chehab 3220c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 3230c0d06caSMauro Carvalho Chehab if (buflen < 9) { 3249e56380aSJoe Perches uvc_dbg(dev, DESCR, 325ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d FORMAT error\n", 3260c0d06caSMauro Carvalho Chehab dev->udev->devnum, 3270c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 3280c0d06caSMauro Carvalho Chehab return -EINVAL; 3290c0d06caSMauro Carvalho Chehab } 3300c0d06caSMauro Carvalho Chehab 33150459f10SLaurent Pinchart if ((buffer[8] & 0x7f) > 2) { 3329e56380aSJoe Perches uvc_dbg(dev, DESCR, 333ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d: unknown DV format %u\n", 3340c0d06caSMauro Carvalho Chehab dev->udev->devnum, 3350c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, buffer[8]); 3360c0d06caSMauro Carvalho Chehab return -EINVAL; 3370c0d06caSMauro Carvalho Chehab } 3380c0d06caSMauro Carvalho Chehab 3390c0d06caSMauro Carvalho Chehab format->fcc = V4L2_PIX_FMT_DV; 3400c0d06caSMauro Carvalho Chehab format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM; 3410c0d06caSMauro Carvalho Chehab format->bpp = 0; 3420c0d06caSMauro Carvalho Chehab ftype = 0; 3430c0d06caSMauro Carvalho Chehab 3440c0d06caSMauro Carvalho Chehab /* Create a dummy frame descriptor. */ 345af621ba2SLaurent Pinchart frame = &frames[0]; 346af621ba2SLaurent Pinchart memset(frame, 0, sizeof(*frame)); 3470c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = 1; 3480c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 1; 3490c0d06caSMauro Carvalho Chehab frame->dwFrameInterval = *intervals; 3500c0d06caSMauro Carvalho Chehab *(*intervals)++ = 1; 3510c0d06caSMauro Carvalho Chehab format->nframes = 1; 3520c0d06caSMauro Carvalho Chehab break; 3530c0d06caSMauro Carvalho Chehab 3540c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MPEG2TS: 3550c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_STREAM_BASED: 3560c0d06caSMauro Carvalho Chehab /* Not supported yet. */ 3570c0d06caSMauro Carvalho Chehab default: 3589e56380aSJoe Perches uvc_dbg(dev, DESCR, 359ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d unsupported format %u\n", 3600c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber, 3610c0d06caSMauro Carvalho Chehab buffer[2]); 3620c0d06caSMauro Carvalho Chehab return -EINVAL; 3630c0d06caSMauro Carvalho Chehab } 3640c0d06caSMauro Carvalho Chehab 36550459f10SLaurent Pinchart uvc_dbg(dev, DESCR, "Found format %p4cc", &format->fcc); 3660c0d06caSMauro Carvalho Chehab 3670c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 3680c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 3690c0d06caSMauro Carvalho Chehab 370699b9a86SLaurent Pinchart /* 371699b9a86SLaurent Pinchart * Parse the frame descriptors. Only uncompressed, MJPEG and frame 3720c0d06caSMauro Carvalho Chehab * based formats have frame descriptors. 3730c0d06caSMauro Carvalho Chehab */ 374575a562fSBenoit Sevens while (ftype && buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 3750c0d06caSMauro Carvalho Chehab buffer[2] == ftype) { 3767691d900SLaurent Pinchart unsigned int maxIntervalIndex; 3777691d900SLaurent Pinchart 378af621ba2SLaurent Pinchart frame = &frames[format->nframes]; 3790c0d06caSMauro Carvalho Chehab if (ftype != UVC_VS_FRAME_FRAME_BASED) 3800c0d06caSMauro Carvalho Chehab n = buflen > 25 ? buffer[25] : 0; 3810c0d06caSMauro Carvalho Chehab else 3820c0d06caSMauro Carvalho Chehab n = buflen > 21 ? buffer[21] : 0; 3830c0d06caSMauro Carvalho Chehab 3840c0d06caSMauro Carvalho Chehab n = n ? n : 3; 3850c0d06caSMauro Carvalho Chehab 3860c0d06caSMauro Carvalho Chehab if (buflen < 26 + 4*n) { 3879e56380aSJoe Perches uvc_dbg(dev, DESCR, 388ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d FRAME error\n", 389ed4c5fa4SRicardo Ribalda dev->udev->devnum, 3900c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 3910c0d06caSMauro Carvalho Chehab return -EINVAL; 3920c0d06caSMauro Carvalho Chehab } 3930c0d06caSMauro Carvalho Chehab 3940c0d06caSMauro Carvalho Chehab frame->bFrameIndex = buffer[3]; 3950c0d06caSMauro Carvalho Chehab frame->bmCapabilities = buffer[4]; 396e1b78a33SPhilipp Zabel frame->wWidth = get_unaligned_le16(&buffer[5]) 397e1b78a33SPhilipp Zabel * width_multiplier; 3980c0d06caSMauro Carvalho Chehab frame->wHeight = get_unaligned_le16(&buffer[7]); 3990c0d06caSMauro Carvalho Chehab frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); 4000c0d06caSMauro Carvalho Chehab frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); 4010c0d06caSMauro Carvalho Chehab if (ftype != UVC_VS_FRAME_FRAME_BASED) { 4020c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = 4030c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[17]); 4040c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 4050c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[21]); 4060c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = buffer[25]; 4070c0d06caSMauro Carvalho Chehab } else { 4080c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = 0; 4090c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 4100c0d06caSMauro Carvalho Chehab get_unaligned_le32(&buffer[17]); 4110c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = buffer[21]; 4120c0d06caSMauro Carvalho Chehab } 413c9d597b9SLaurent Pinchart 414c9d597b9SLaurent Pinchart /* 415c9d597b9SLaurent Pinchart * Copy the frame intervals. 416c9d597b9SLaurent Pinchart * 417c9d597b9SLaurent Pinchart * Some bogus devices report dwMinFrameInterval equal to 418c9d597b9SLaurent Pinchart * dwMaxFrameInterval and have dwFrameIntervalStep set to 419c9d597b9SLaurent Pinchart * zero. Setting all null intervals to 1 fixes the problem and 420c9d597b9SLaurent Pinchart * some other divisions by zero that could happen. 421c9d597b9SLaurent Pinchart */ 4220c0d06caSMauro Carvalho Chehab frame->dwFrameInterval = *intervals; 4230c0d06caSMauro Carvalho Chehab 424c9d597b9SLaurent Pinchart for (i = 0; i < n; ++i) { 425c9d597b9SLaurent Pinchart interval = get_unaligned_le32(&buffer[26+4*i]); 4267691d900SLaurent Pinchart (*intervals)[i] = interval ? interval : 1; 427c9d597b9SLaurent Pinchart } 428c9d597b9SLaurent Pinchart 429c9d597b9SLaurent Pinchart /* 430c9d597b9SLaurent Pinchart * Apply more fixes, quirks and workarounds to handle incorrect 431c9d597b9SLaurent Pinchart * or broken descriptors. 432c9d597b9SLaurent Pinchart */ 433c9d597b9SLaurent Pinchart 434699b9a86SLaurent Pinchart /* 435699b9a86SLaurent Pinchart * Several UVC chipsets screw up dwMaxVideoFrameBufferSize 4360c0d06caSMauro Carvalho Chehab * completely. Observed behaviours range from setting the 4370c0d06caSMauro Carvalho Chehab * value to 1.1x the actual frame size to hardwiring the 4380c0d06caSMauro Carvalho Chehab * 16 low bits to 0. This results in a higher than necessary 4390c0d06caSMauro Carvalho Chehab * memory usage as well as a wrong image size information. For 4400c0d06caSMauro Carvalho Chehab * uncompressed formats this can be fixed by computing the 4410c0d06caSMauro Carvalho Chehab * value from the frame size. 4420c0d06caSMauro Carvalho Chehab */ 4430c0d06caSMauro Carvalho Chehab if (!(format->flags & UVC_FMT_FLAG_COMPRESSED)) 4440c0d06caSMauro Carvalho Chehab frame->dwMaxVideoFrameBufferSize = format->bpp 4450c0d06caSMauro Carvalho Chehab * frame->wWidth * frame->wHeight / 8; 4460c0d06caSMauro Carvalho Chehab 4477691d900SLaurent Pinchart /* 4487691d900SLaurent Pinchart * Clamp the default frame interval to the boundaries. A zero 4497691d900SLaurent Pinchart * bFrameIntervalType value indicates a continuous frame 4507691d900SLaurent Pinchart * interval range, with dwFrameInterval[0] storing the minimum 4517691d900SLaurent Pinchart * value and dwFrameInterval[1] storing the maximum value. 4527691d900SLaurent Pinchart */ 4537691d900SLaurent Pinchart maxIntervalIndex = frame->bFrameIntervalType ? n - 1 : 1; 4540c0d06caSMauro Carvalho Chehab frame->dwDefaultFrameInterval = 4557c5dfb2dSLaurent Pinchart clamp(frame->dwDefaultFrameInterval, 4567c5dfb2dSLaurent Pinchart frame->dwFrameInterval[0], 4577691d900SLaurent Pinchart frame->dwFrameInterval[maxIntervalIndex]); 4580c0d06caSMauro Carvalho Chehab 459c9d597b9SLaurent Pinchart /* 460c9d597b9SLaurent Pinchart * Some devices report frame intervals that are not functional. 461c9d597b9SLaurent Pinchart * If the corresponding quirk is set, restrict operation to the 462c9d597b9SLaurent Pinchart * first interval only. 463c9d597b9SLaurent Pinchart */ 4640c0d06caSMauro Carvalho Chehab if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) { 4650c0d06caSMauro Carvalho Chehab frame->bFrameIntervalType = 1; 466af621ba2SLaurent Pinchart (*intervals)[0] = frame->dwDefaultFrameInterval; 4670c0d06caSMauro Carvalho Chehab } 4680c0d06caSMauro Carvalho Chehab 4699e56380aSJoe Perches uvc_dbg(dev, DESCR, "- %ux%u (%u.%u fps)\n", 4700c0d06caSMauro Carvalho Chehab frame->wWidth, frame->wHeight, 4710c0d06caSMauro Carvalho Chehab 10000000 / frame->dwDefaultFrameInterval, 4720c0d06caSMauro Carvalho Chehab (100000000 / frame->dwDefaultFrameInterval) % 10); 4730c0d06caSMauro Carvalho Chehab 4740c0d06caSMauro Carvalho Chehab format->nframes++; 4757691d900SLaurent Pinchart *intervals += n; 4767691d900SLaurent Pinchart 4770c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 4780c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 4790c0d06caSMauro Carvalho Chehab } 4800c0d06caSMauro Carvalho Chehab 4810c0d06caSMauro Carvalho Chehab if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 4820c0d06caSMauro Carvalho Chehab buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { 4830c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 4840c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 4850c0d06caSMauro Carvalho Chehab } 4860c0d06caSMauro Carvalho Chehab 4870c0d06caSMauro Carvalho Chehab if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 4880c0d06caSMauro Carvalho Chehab buffer[2] == UVC_VS_COLORFORMAT) { 4890c0d06caSMauro Carvalho Chehab if (buflen < 6) { 4909e56380aSJoe Perches uvc_dbg(dev, DESCR, 491ed4c5fa4SRicardo Ribalda "device %d videostreaming interface %d COLORFORMAT error\n", 4920c0d06caSMauro Carvalho Chehab dev->udev->devnum, 4930c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber); 4940c0d06caSMauro Carvalho Chehab return -EINVAL; 4950c0d06caSMauro Carvalho Chehab } 4960c0d06caSMauro Carvalho Chehab 4970c0d06caSMauro Carvalho Chehab format->colorspace = uvc_colorspace(buffer[3]); 498ec2c23f6SAdam Goode format->xfer_func = uvc_xfer_func(buffer[4]); 499ec2c23f6SAdam Goode format->ycbcr_enc = uvc_ycbcr_enc(buffer[5]); 5000c0d06caSMauro Carvalho Chehab 5010c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 5020c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 503e82822faSRicardo Ribalda } else { 504e82822faSRicardo Ribalda format->colorspace = V4L2_COLORSPACE_SRGB; 5050c0d06caSMauro Carvalho Chehab } 5060c0d06caSMauro Carvalho Chehab 5070c0d06caSMauro Carvalho Chehab return buffer - start; 5080c0d06caSMauro Carvalho Chehab } 5090c0d06caSMauro Carvalho Chehab 5100c0d06caSMauro Carvalho Chehab static int uvc_parse_streaming(struct uvc_device *dev, 5110c0d06caSMauro Carvalho Chehab struct usb_interface *intf) 5120c0d06caSMauro Carvalho Chehab { 5130c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming = NULL; 5140c0d06caSMauro Carvalho Chehab struct uvc_format *format; 5150c0d06caSMauro Carvalho Chehab struct uvc_frame *frame; 5160c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = &intf->altsetting[0]; 517102df33eSLaurent Pinchart const unsigned char *_buffer, *buffer = alts->extra; 5180c0d06caSMauro Carvalho Chehab int _buflen, buflen = alts->extralen; 5190c0d06caSMauro Carvalho Chehab unsigned int nformats = 0, nframes = 0, nintervals = 0; 5200c0d06caSMauro Carvalho Chehab unsigned int size, i, n, p; 5212c6b222cSLaurent Pinchart u32 *interval; 5222c6b222cSLaurent Pinchart u16 psize; 5230c0d06caSMauro Carvalho Chehab int ret = -EINVAL; 5240c0d06caSMauro Carvalho Chehab 5250c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass 5260c0d06caSMauro Carvalho Chehab != UVC_SC_VIDEOSTREAMING) { 5279e56380aSJoe Perches uvc_dbg(dev, DESCR, 528ed4c5fa4SRicardo Ribalda "device %d interface %d isn't a video streaming interface\n", 529ed4c5fa4SRicardo Ribalda dev->udev->devnum, 5300c0d06caSMauro Carvalho Chehab intf->altsetting[0].desc.bInterfaceNumber); 5310c0d06caSMauro Carvalho Chehab return -EINVAL; 5320c0d06caSMauro Carvalho Chehab } 5330c0d06caSMauro Carvalho Chehab 5340c0d06caSMauro Carvalho Chehab if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) { 5359e56380aSJoe Perches uvc_dbg(dev, DESCR, 536ed4c5fa4SRicardo Ribalda "device %d interface %d is already claimed\n", 537ed4c5fa4SRicardo Ribalda dev->udev->devnum, 5380c0d06caSMauro Carvalho Chehab intf->altsetting[0].desc.bInterfaceNumber); 5390c0d06caSMauro Carvalho Chehab return -EINVAL; 5400c0d06caSMauro Carvalho Chehab } 5410c0d06caSMauro Carvalho Chehab 542ece41454SKieran Bingham streaming = uvc_stream_new(dev, intf); 5430c0d06caSMauro Carvalho Chehab if (streaming == NULL) { 5440c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, intf); 545ece41454SKieran Bingham return -ENOMEM; 5460c0d06caSMauro Carvalho Chehab } 5470c0d06caSMauro Carvalho Chehab 548699b9a86SLaurent Pinchart /* 549699b9a86SLaurent Pinchart * The Pico iMage webcam has its class-specific interface descriptors 5500c0d06caSMauro Carvalho Chehab * after the endpoint descriptors. 5510c0d06caSMauro Carvalho Chehab */ 5520c0d06caSMauro Carvalho Chehab if (buflen == 0) { 5530c0d06caSMauro Carvalho Chehab for (i = 0; i < alts->desc.bNumEndpoints; ++i) { 5540c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep = &alts->endpoint[i]; 5550c0d06caSMauro Carvalho Chehab 5560c0d06caSMauro Carvalho Chehab if (ep->extralen == 0) 5570c0d06caSMauro Carvalho Chehab continue; 5580c0d06caSMauro Carvalho Chehab 5590c0d06caSMauro Carvalho Chehab if (ep->extralen > 2 && 5600c0d06caSMauro Carvalho Chehab ep->extra[1] == USB_DT_CS_INTERFACE) { 5619e56380aSJoe Perches uvc_dbg(dev, DESCR, 5629e56380aSJoe Perches "trying extra data from endpoint %u\n", 563ed4c5fa4SRicardo Ribalda i); 5640c0d06caSMauro Carvalho Chehab buffer = alts->endpoint[i].extra; 5650c0d06caSMauro Carvalho Chehab buflen = alts->endpoint[i].extralen; 5660c0d06caSMauro Carvalho Chehab break; 5670c0d06caSMauro Carvalho Chehab } 5680c0d06caSMauro Carvalho Chehab } 5690c0d06caSMauro Carvalho Chehab } 5700c0d06caSMauro Carvalho Chehab 5710c0d06caSMauro Carvalho Chehab /* Skip the standard interface descriptors. */ 5720c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) { 5730c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 5740c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 5750c0d06caSMauro Carvalho Chehab } 5760c0d06caSMauro Carvalho Chehab 5770c0d06caSMauro Carvalho Chehab if (buflen <= 2) { 5789e56380aSJoe Perches uvc_dbg(dev, DESCR, 5799e56380aSJoe Perches "no class-specific streaming interface descriptors found\n"); 5800c0d06caSMauro Carvalho Chehab goto error; 5810c0d06caSMauro Carvalho Chehab } 5820c0d06caSMauro Carvalho Chehab 5830c0d06caSMauro Carvalho Chehab /* Parse the header descriptor. */ 5840c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 5850c0d06caSMauro Carvalho Chehab case UVC_VS_OUTPUT_HEADER: 5860c0d06caSMauro Carvalho Chehab streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 5870c0d06caSMauro Carvalho Chehab size = 9; 5880c0d06caSMauro Carvalho Chehab break; 5890c0d06caSMauro Carvalho Chehab 5900c0d06caSMauro Carvalho Chehab case UVC_VS_INPUT_HEADER: 5910c0d06caSMauro Carvalho Chehab streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 5920c0d06caSMauro Carvalho Chehab size = 13; 5930c0d06caSMauro Carvalho Chehab break; 5940c0d06caSMauro Carvalho Chehab 5950c0d06caSMauro Carvalho Chehab default: 5969e56380aSJoe Perches uvc_dbg(dev, DESCR, 5979e56380aSJoe Perches "device %d videostreaming interface %d HEADER descriptor not found\n", 598ed4c5fa4SRicardo Ribalda dev->udev->devnum, alts->desc.bInterfaceNumber); 5990c0d06caSMauro Carvalho Chehab goto error; 6000c0d06caSMauro Carvalho Chehab } 6010c0d06caSMauro Carvalho Chehab 6020c0d06caSMauro Carvalho Chehab p = buflen >= 4 ? buffer[3] : 0; 6030c0d06caSMauro Carvalho Chehab n = buflen >= size ? buffer[size-1] : 0; 6040c0d06caSMauro Carvalho Chehab 6050c0d06caSMauro Carvalho Chehab if (buflen < size + p*n) { 6069e56380aSJoe Perches uvc_dbg(dev, DESCR, 6079e56380aSJoe Perches "device %d videostreaming interface %d HEADER descriptor is invalid\n", 6080c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber); 6090c0d06caSMauro Carvalho Chehab goto error; 6100c0d06caSMauro Carvalho Chehab } 6110c0d06caSMauro Carvalho Chehab 6120c0d06caSMauro Carvalho Chehab streaming->header.bNumFormats = p; 6130c0d06caSMauro Carvalho Chehab streaming->header.bEndpointAddress = buffer[6]; 6140c0d06caSMauro Carvalho Chehab if (buffer[2] == UVC_VS_INPUT_HEADER) { 6150c0d06caSMauro Carvalho Chehab streaming->header.bmInfo = buffer[7]; 6160c0d06caSMauro Carvalho Chehab streaming->header.bTerminalLink = buffer[8]; 6170c0d06caSMauro Carvalho Chehab streaming->header.bStillCaptureMethod = buffer[9]; 6180c0d06caSMauro Carvalho Chehab streaming->header.bTriggerSupport = buffer[10]; 6190c0d06caSMauro Carvalho Chehab streaming->header.bTriggerUsage = buffer[11]; 6200c0d06caSMauro Carvalho Chehab } else { 6210c0d06caSMauro Carvalho Chehab streaming->header.bTerminalLink = buffer[7]; 6220c0d06caSMauro Carvalho Chehab } 6230c0d06caSMauro Carvalho Chehab streaming->header.bControlSize = n; 6240c0d06caSMauro Carvalho Chehab 6250c0d06caSMauro Carvalho Chehab streaming->header.bmaControls = kmemdup(&buffer[size], p * n, 6260c0d06caSMauro Carvalho Chehab GFP_KERNEL); 6270c0d06caSMauro Carvalho Chehab if (streaming->header.bmaControls == NULL) { 6280c0d06caSMauro Carvalho Chehab ret = -ENOMEM; 6290c0d06caSMauro Carvalho Chehab goto error; 6300c0d06caSMauro Carvalho Chehab } 6310c0d06caSMauro Carvalho Chehab 6320c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 6330c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 6340c0d06caSMauro Carvalho Chehab 6350c0d06caSMauro Carvalho Chehab _buffer = buffer; 6360c0d06caSMauro Carvalho Chehab _buflen = buflen; 6370c0d06caSMauro Carvalho Chehab 6380c0d06caSMauro Carvalho Chehab /* Count the format and frame descriptors. */ 6390c0d06caSMauro Carvalho Chehab while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) { 6400c0d06caSMauro Carvalho Chehab switch (_buffer[2]) { 6410c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 6420c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 6430c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 6440c0d06caSMauro Carvalho Chehab nformats++; 6450c0d06caSMauro Carvalho Chehab break; 6460c0d06caSMauro Carvalho Chehab 6470c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 648699b9a86SLaurent Pinchart /* 649699b9a86SLaurent Pinchart * DV format has no frame descriptor. We will create a 6500c0d06caSMauro Carvalho Chehab * dummy frame descriptor with a dummy frame interval. 6510c0d06caSMauro Carvalho Chehab */ 6520c0d06caSMauro Carvalho Chehab nformats++; 6530c0d06caSMauro Carvalho Chehab nframes++; 6540c0d06caSMauro Carvalho Chehab nintervals++; 6550c0d06caSMauro Carvalho Chehab break; 6560c0d06caSMauro Carvalho Chehab 6570c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MPEG2TS: 6580c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_STREAM_BASED: 6599e56380aSJoe Perches uvc_dbg(dev, DESCR, 6609e56380aSJoe Perches "device %d videostreaming interface %d FORMAT %u is not supported\n", 6610c0d06caSMauro Carvalho Chehab dev->udev->devnum, 6620c0d06caSMauro Carvalho Chehab alts->desc.bInterfaceNumber, _buffer[2]); 6630c0d06caSMauro Carvalho Chehab break; 6640c0d06caSMauro Carvalho Chehab 6650c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_UNCOMPRESSED: 6660c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_MJPEG: 6670c0d06caSMauro Carvalho Chehab nframes++; 6680c0d06caSMauro Carvalho Chehab if (_buflen > 25) 6690c0d06caSMauro Carvalho Chehab nintervals += _buffer[25] ? _buffer[25] : 3; 6700c0d06caSMauro Carvalho Chehab break; 6710c0d06caSMauro Carvalho Chehab 6720c0d06caSMauro Carvalho Chehab case UVC_VS_FRAME_FRAME_BASED: 6730c0d06caSMauro Carvalho Chehab nframes++; 6740c0d06caSMauro Carvalho Chehab if (_buflen > 21) 6750c0d06caSMauro Carvalho Chehab nintervals += _buffer[21] ? _buffer[21] : 3; 6760c0d06caSMauro Carvalho Chehab break; 6770c0d06caSMauro Carvalho Chehab } 6780c0d06caSMauro Carvalho Chehab 6790c0d06caSMauro Carvalho Chehab _buflen -= _buffer[0]; 6800c0d06caSMauro Carvalho Chehab _buffer += _buffer[0]; 6810c0d06caSMauro Carvalho Chehab } 6820c0d06caSMauro Carvalho Chehab 6830c0d06caSMauro Carvalho Chehab if (nformats == 0) { 6849e56380aSJoe Perches uvc_dbg(dev, DESCR, 6859e56380aSJoe Perches "device %d videostreaming interface %d has no supported formats defined\n", 6860c0d06caSMauro Carvalho Chehab dev->udev->devnum, alts->desc.bInterfaceNumber); 6870c0d06caSMauro Carvalho Chehab goto error; 6880c0d06caSMauro Carvalho Chehab } 6890c0d06caSMauro Carvalho Chehab 690d8915d27SRicardo Ribalda /* 691d8915d27SRicardo Ribalda * Allocate memory for the formats, the frames and the intervals, 692d8915d27SRicardo Ribalda * plus any required padding to guarantee that everything has the 693d8915d27SRicardo Ribalda * correct alignment. 694d8915d27SRicardo Ribalda */ 695d8915d27SRicardo Ribalda size = nformats * sizeof(*format); 696d8915d27SRicardo Ribalda size = ALIGN(size, __alignof__(*frame)) + nframes * sizeof(*frame); 697d8915d27SRicardo Ribalda size = ALIGN(size, __alignof__(*interval)) 698f14d4988SLaurent Pinchart + nintervals * sizeof(*interval); 699d8915d27SRicardo Ribalda 7000c0d06caSMauro Carvalho Chehab format = kzalloc(size, GFP_KERNEL); 701d8915d27SRicardo Ribalda if (!format) { 7020c0d06caSMauro Carvalho Chehab ret = -ENOMEM; 7030c0d06caSMauro Carvalho Chehab goto error; 7040c0d06caSMauro Carvalho Chehab } 7050c0d06caSMauro Carvalho Chehab 706d8915d27SRicardo Ribalda frame = (void *)format + nformats * sizeof(*format); 707d8915d27SRicardo Ribalda frame = PTR_ALIGN(frame, __alignof__(*frame)); 708d8915d27SRicardo Ribalda interval = (void *)frame + nframes * sizeof(*frame); 709d8915d27SRicardo Ribalda interval = PTR_ALIGN(interval, __alignof__(*interval)); 7100c0d06caSMauro Carvalho Chehab 711ccfad4e8SLaurent Pinchart streaming->formats = format; 71281f3affaSLaurent Pinchart streaming->nformats = 0; 7130c0d06caSMauro Carvalho Chehab 7140c0d06caSMauro Carvalho Chehab /* Parse the format descriptors. */ 7150c0d06caSMauro Carvalho Chehab while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) { 7160c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 7170c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_UNCOMPRESSED: 7180c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_MJPEG: 7190c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_DV: 7200c0d06caSMauro Carvalho Chehab case UVC_VS_FORMAT_FRAME_BASED: 721af621ba2SLaurent Pinchart ret = uvc_parse_format(dev, streaming, format, frame, 7220c0d06caSMauro Carvalho Chehab &interval, buffer, buflen); 7230c0d06caSMauro Carvalho Chehab if (ret < 0) 7240c0d06caSMauro Carvalho Chehab goto error; 72581f3affaSLaurent Pinchart if (!ret) 72681f3affaSLaurent Pinchart break; 7270c0d06caSMauro Carvalho Chehab 72881f3affaSLaurent Pinchart streaming->nformats++; 7290c0d06caSMauro Carvalho Chehab frame += format->nframes; 7300c0d06caSMauro Carvalho Chehab format++; 7310c0d06caSMauro Carvalho Chehab 7320c0d06caSMauro Carvalho Chehab buflen -= ret; 7330c0d06caSMauro Carvalho Chehab buffer += ret; 7340c0d06caSMauro Carvalho Chehab continue; 7350c0d06caSMauro Carvalho Chehab 7360c0d06caSMauro Carvalho Chehab default: 7370c0d06caSMauro Carvalho Chehab break; 7380c0d06caSMauro Carvalho Chehab } 7390c0d06caSMauro Carvalho Chehab 7400c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 7410c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 7420c0d06caSMauro Carvalho Chehab } 7430c0d06caSMauro Carvalho Chehab 7440c0d06caSMauro Carvalho Chehab if (buflen) 7459e56380aSJoe Perches uvc_dbg(dev, DESCR, 7469e56380aSJoe Perches "device %d videostreaming interface %d has %u bytes of trailing descriptor garbage\n", 7479e56380aSJoe Perches dev->udev->devnum, alts->desc.bInterfaceNumber, buflen); 7480c0d06caSMauro Carvalho Chehab 7490c0d06caSMauro Carvalho Chehab /* Parse the alternate settings to find the maximum bandwidth. */ 7500c0d06caSMauro Carvalho Chehab for (i = 0; i < intf->num_altsetting; ++i) { 7510c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep; 7522a8c1952SPedro Guilherme Siqueira Moreira 7530c0d06caSMauro Carvalho Chehab alts = &intf->altsetting[i]; 7540c0d06caSMauro Carvalho Chehab ep = uvc_find_endpoint(alts, 7550c0d06caSMauro Carvalho Chehab streaming->header.bEndpointAddress); 7560c0d06caSMauro Carvalho Chehab if (ep == NULL) 7570c0d06caSMauro Carvalho Chehab continue; 7585b9c75c7SRicardo Ribalda psize = uvc_endpoint_max_bpi(dev->udev, ep); 7590c0d06caSMauro Carvalho Chehab if (psize > streaming->maxpsize) 7600c0d06caSMauro Carvalho Chehab streaming->maxpsize = psize; 7610c0d06caSMauro Carvalho Chehab } 7620c0d06caSMauro Carvalho Chehab 7630c0d06caSMauro Carvalho Chehab list_add_tail(&streaming->list, &dev->streams); 7640c0d06caSMauro Carvalho Chehab return 0; 7650c0d06caSMauro Carvalho Chehab 7660c0d06caSMauro Carvalho Chehab error: 7670c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, intf); 768ece41454SKieran Bingham uvc_stream_delete(streaming); 7690c0d06caSMauro Carvalho Chehab return ret; 7700c0d06caSMauro Carvalho Chehab } 7710c0d06caSMauro Carvalho Chehab 772351509c6SRicardo Ribalda static const u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA; 7732886477fSRicardo Ribalda static const u8 uvc_gpio_guid[16] = UVC_GUID_EXT_GPIO_CONTROLLER; 774351509c6SRicardo Ribalda static const u8 uvc_media_transport_input_guid[16] = 775351509c6SRicardo Ribalda UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT; 776351509c6SRicardo Ribalda static const u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING; 777351509c6SRicardo Ribalda 778b11813bcSThadeu Lima de Souza Cascardo static struct uvc_entity *uvc_alloc_new_entity(struct uvc_device *dev, u16 type, 779b11813bcSThadeu Lima de Souza Cascardo u16 id, unsigned int num_pads, 780b11813bcSThadeu Lima de Souza Cascardo unsigned int extra_size) 7810c0d06caSMauro Carvalho Chehab { 7820c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 7830c0d06caSMauro Carvalho Chehab unsigned int num_inputs; 7840c0d06caSMauro Carvalho Chehab unsigned int size; 7850c0d06caSMauro Carvalho Chehab unsigned int i; 7860c0d06caSMauro Carvalho Chehab 787b11813bcSThadeu Lima de Souza Cascardo /* Per UVC 1.1+ spec 3.7.2, the ID should be non-zero. */ 788b11813bcSThadeu Lima de Souza Cascardo if (id == 0) { 789b11813bcSThadeu Lima de Souza Cascardo dev_err(&dev->udev->dev, "Found Unit with invalid ID 0.\n"); 790b11813bcSThadeu Lima de Souza Cascardo return ERR_PTR(-EINVAL); 791b11813bcSThadeu Lima de Souza Cascardo } 792b11813bcSThadeu Lima de Souza Cascardo 793b11813bcSThadeu Lima de Souza Cascardo /* Per UVC 1.1+ spec 3.7.2, the ID is unique. */ 794b11813bcSThadeu Lima de Souza Cascardo if (uvc_entity_by_id(dev, id)) { 795b11813bcSThadeu Lima de Souza Cascardo dev_err(&dev->udev->dev, "Found multiple Units with ID %u\n", id); 796b11813bcSThadeu Lima de Souza Cascardo return ERR_PTR(-EINVAL); 797b11813bcSThadeu Lima de Souza Cascardo } 798b11813bcSThadeu Lima de Souza Cascardo 79989dd34caSNadav Amit extra_size = roundup(extra_size, sizeof(*entity->pads)); 8007532dad6SRicardo Ribalda if (num_pads) 8017532dad6SRicardo Ribalda num_inputs = type & UVC_TERM_OUTPUT ? num_pads : num_pads - 1; 8027532dad6SRicardo Ribalda else 8037532dad6SRicardo Ribalda num_inputs = 0; 8040c0d06caSMauro Carvalho Chehab size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads 8050c0d06caSMauro Carvalho Chehab + num_inputs; 8060c0d06caSMauro Carvalho Chehab entity = kzalloc(size, GFP_KERNEL); 8070c0d06caSMauro Carvalho Chehab if (entity == NULL) 808b11813bcSThadeu Lima de Souza Cascardo return ERR_PTR(-ENOMEM); 8090c0d06caSMauro Carvalho Chehab 8100c0d06caSMauro Carvalho Chehab entity->id = id; 8110c0d06caSMauro Carvalho Chehab entity->type = type; 8120c0d06caSMauro Carvalho Chehab 813351509c6SRicardo Ribalda /* 814351509c6SRicardo Ribalda * Set the GUID for standard entity types. For extension units, the GUID 815351509c6SRicardo Ribalda * is initialized by the caller. 816351509c6SRicardo Ribalda */ 817351509c6SRicardo Ribalda switch (type) { 8182886477fSRicardo Ribalda case UVC_EXT_GPIO_UNIT: 8192886477fSRicardo Ribalda memcpy(entity->guid, uvc_gpio_guid, 16); 8202886477fSRicardo Ribalda break; 821351509c6SRicardo Ribalda case UVC_ITT_CAMERA: 822351509c6SRicardo Ribalda memcpy(entity->guid, uvc_camera_guid, 16); 823351509c6SRicardo Ribalda break; 824351509c6SRicardo Ribalda case UVC_ITT_MEDIA_TRANSPORT_INPUT: 825351509c6SRicardo Ribalda memcpy(entity->guid, uvc_media_transport_input_guid, 16); 826351509c6SRicardo Ribalda break; 827351509c6SRicardo Ribalda case UVC_VC_PROCESSING_UNIT: 828351509c6SRicardo Ribalda memcpy(entity->guid, uvc_processing_guid, 16); 829351509c6SRicardo Ribalda break; 830351509c6SRicardo Ribalda } 831351509c6SRicardo Ribalda 8320c0d06caSMauro Carvalho Chehab entity->num_links = 0; 8330c0d06caSMauro Carvalho Chehab entity->num_pads = num_pads; 8340c0d06caSMauro Carvalho Chehab entity->pads = ((void *)(entity + 1)) + extra_size; 8350c0d06caSMauro Carvalho Chehab 8360c0d06caSMauro Carvalho Chehab for (i = 0; i < num_inputs; ++i) 8370c0d06caSMauro Carvalho Chehab entity->pads[i].flags = MEDIA_PAD_FL_SINK; 8387532dad6SRicardo Ribalda if (!UVC_ENTITY_IS_OTERM(entity) && num_pads) 8390c0d06caSMauro Carvalho Chehab entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE; 8400c0d06caSMauro Carvalho Chehab 8410c0d06caSMauro Carvalho Chehab entity->bNrInPins = num_inputs; 8422c6b222cSLaurent Pinchart entity->baSourceID = (u8 *)(&entity->pads[num_pads]); 8430c0d06caSMauro Carvalho Chehab 8440c0d06caSMauro Carvalho Chehab return entity; 8450c0d06caSMauro Carvalho Chehab } 8460c0d06caSMauro Carvalho Chehab 8475f0e659dSLaurent Pinchart static void uvc_entity_set_name(struct uvc_device *dev, struct uvc_entity *entity, 8485f0e659dSLaurent Pinchart const char *type_name, u8 string_id) 8495f0e659dSLaurent Pinchart { 8505f0e659dSLaurent Pinchart int ret; 8515f0e659dSLaurent Pinchart 8525f0e659dSLaurent Pinchart /* 8535f0e659dSLaurent Pinchart * First attempt to read the entity name from the device. If the entity 8545f0e659dSLaurent Pinchart * has no associated string, or if reading the string fails (most 8555f0e659dSLaurent Pinchart * likely due to a buggy firmware), fall back to default names based on 8565f0e659dSLaurent Pinchart * the entity type. 8575f0e659dSLaurent Pinchart */ 8585f0e659dSLaurent Pinchart if (string_id) { 8595f0e659dSLaurent Pinchart ret = usb_string(dev->udev, string_id, entity->name, 8605f0e659dSLaurent Pinchart sizeof(entity->name)); 8615f0e659dSLaurent Pinchart if (!ret) 8625f0e659dSLaurent Pinchart return; 8635f0e659dSLaurent Pinchart } 8645f0e659dSLaurent Pinchart 8655f0e659dSLaurent Pinchart sprintf(entity->name, "%s %u", type_name, entity->id); 8665f0e659dSLaurent Pinchart } 8675f0e659dSLaurent Pinchart 8680c0d06caSMauro Carvalho Chehab /* Parse vendor-specific extensions. */ 8690c0d06caSMauro Carvalho Chehab static int uvc_parse_vendor_control(struct uvc_device *dev, 8700c0d06caSMauro Carvalho Chehab const unsigned char *buffer, int buflen) 8710c0d06caSMauro Carvalho Chehab { 8720c0d06caSMauro Carvalho Chehab struct usb_device *udev = dev->udev; 8730c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 8740c0d06caSMauro Carvalho Chehab struct uvc_entity *unit; 8750c0d06caSMauro Carvalho Chehab unsigned int n, p; 8760c0d06caSMauro Carvalho Chehab int handled = 0; 8770c0d06caSMauro Carvalho Chehab 8780c0d06caSMauro Carvalho Chehab switch (le16_to_cpu(dev->udev->descriptor.idVendor)) { 8790c0d06caSMauro Carvalho Chehab case 0x046d: /* Logitech */ 8800c0d06caSMauro Carvalho Chehab if (buffer[1] != 0x41 || buffer[2] != 0x01) 8810c0d06caSMauro Carvalho Chehab break; 8820c0d06caSMauro Carvalho Chehab 883699b9a86SLaurent Pinchart /* 884699b9a86SLaurent Pinchart * Logitech implements several vendor specific functions 8850c0d06caSMauro Carvalho Chehab * through vendor specific extension units (LXU). 8860c0d06caSMauro Carvalho Chehab * 8870c0d06caSMauro Carvalho Chehab * The LXU descriptors are similar to XU descriptors 8880c0d06caSMauro Carvalho Chehab * (see "USB Device Video Class for Video Devices", section 8890c0d06caSMauro Carvalho Chehab * 3.7.2.6 "Extension Unit Descriptor") with the following 8900c0d06caSMauro Carvalho Chehab * differences: 8910c0d06caSMauro Carvalho Chehab * 8920c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 8930c0d06caSMauro Carvalho Chehab * 0 bLength 1 Number 8940c0d06caSMauro Carvalho Chehab * Size of this descriptor, in bytes: 24+p+n*2 8950c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 8960c0d06caSMauro Carvalho Chehab * 23+p+n bmControlsType N Bitmap 8970c0d06caSMauro Carvalho Chehab * Individual bits in the set are defined: 8980c0d06caSMauro Carvalho Chehab * 0: Absolute 8990c0d06caSMauro Carvalho Chehab * 1: Relative 9000c0d06caSMauro Carvalho Chehab * 9010c0d06caSMauro Carvalho Chehab * This bitset is mapped exactly the same as bmControls. 9020c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 9030c0d06caSMauro Carvalho Chehab * 23+p+n*2 bReserved 1 Boolean 9040c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 9050c0d06caSMauro Carvalho Chehab * 24+p+n*2 iExtension 1 Index 9060c0d06caSMauro Carvalho Chehab * Index of a string descriptor that describes this 9070c0d06caSMauro Carvalho Chehab * extension unit. 9080c0d06caSMauro Carvalho Chehab * ---------------------------------------------------------- 9090c0d06caSMauro Carvalho Chehab */ 9100c0d06caSMauro Carvalho Chehab p = buflen >= 22 ? buffer[21] : 0; 9110c0d06caSMauro Carvalho Chehab n = buflen >= 25 + p ? buffer[22+p] : 0; 9120c0d06caSMauro Carvalho Chehab 9130c0d06caSMauro Carvalho Chehab if (buflen < 25 + p + 2*n) { 9149e56380aSJoe Perches uvc_dbg(dev, DESCR, 915ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d EXTENSION_UNIT error\n", 9160c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 9170c0d06caSMauro Carvalho Chehab break; 9180c0d06caSMauro Carvalho Chehab } 9190c0d06caSMauro Carvalho Chehab 920b11813bcSThadeu Lima de Souza Cascardo unit = uvc_alloc_new_entity(dev, UVC_VC_EXTENSION_UNIT, 921b11813bcSThadeu Lima de Souza Cascardo buffer[3], p + 1, 2 * n); 922b11813bcSThadeu Lima de Souza Cascardo if (IS_ERR(unit)) 923b11813bcSThadeu Lima de Souza Cascardo return PTR_ERR(unit); 9240c0d06caSMauro Carvalho Chehab 925351509c6SRicardo Ribalda memcpy(unit->guid, &buffer[4], 16); 9260c0d06caSMauro Carvalho Chehab unit->extension.bNumControls = buffer[20]; 9270c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[22], p); 9280c0d06caSMauro Carvalho Chehab unit->extension.bControlSize = buffer[22+p]; 9292c6b222cSLaurent Pinchart unit->extension.bmControls = (u8 *)unit + sizeof(*unit); 9302c6b222cSLaurent Pinchart unit->extension.bmControlsType = (u8 *)unit + sizeof(*unit) 9310c0d06caSMauro Carvalho Chehab + n; 9320c0d06caSMauro Carvalho Chehab memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); 9330c0d06caSMauro Carvalho Chehab 9345f0e659dSLaurent Pinchart uvc_entity_set_name(dev, unit, "Extension", buffer[24+p+2*n]); 9350c0d06caSMauro Carvalho Chehab 9360c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 9370c0d06caSMauro Carvalho Chehab handled = 1; 9380c0d06caSMauro Carvalho Chehab break; 9390c0d06caSMauro Carvalho Chehab } 9400c0d06caSMauro Carvalho Chehab 9410c0d06caSMauro Carvalho Chehab return handled; 9420c0d06caSMauro Carvalho Chehab } 9430c0d06caSMauro Carvalho Chehab 9440c0d06caSMauro Carvalho Chehab static int uvc_parse_standard_control(struct uvc_device *dev, 9450c0d06caSMauro Carvalho Chehab const unsigned char *buffer, int buflen) 9460c0d06caSMauro Carvalho Chehab { 9470c0d06caSMauro Carvalho Chehab struct usb_device *udev = dev->udev; 9480c0d06caSMauro Carvalho Chehab struct uvc_entity *unit, *term; 9490c0d06caSMauro Carvalho Chehab struct usb_interface *intf; 9500c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 9510c0d06caSMauro Carvalho Chehab unsigned int i, n, p, len; 9525f0e659dSLaurent Pinchart const char *type_name; 9532c6b222cSLaurent Pinchart u16 type; 9540c0d06caSMauro Carvalho Chehab 9550c0d06caSMauro Carvalho Chehab switch (buffer[2]) { 9560c0d06caSMauro Carvalho Chehab case UVC_VC_HEADER: 9570c0d06caSMauro Carvalho Chehab n = buflen >= 12 ? buffer[11] : 0; 9580c0d06caSMauro Carvalho Chehab 959daf41ac2SOliver Neukum if (buflen < 12 + n) { 9609e56380aSJoe Perches uvc_dbg(dev, DESCR, 961ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d HEADER error\n", 962ed4c5fa4SRicardo Ribalda udev->devnum, alts->desc.bInterfaceNumber); 9630c0d06caSMauro Carvalho Chehab return -EINVAL; 9640c0d06caSMauro Carvalho Chehab } 9650c0d06caSMauro Carvalho Chehab 9660c0d06caSMauro Carvalho Chehab dev->uvc_version = get_unaligned_le16(&buffer[3]); 9670c0d06caSMauro Carvalho Chehab dev->clock_frequency = get_unaligned_le32(&buffer[7]); 9680c0d06caSMauro Carvalho Chehab 9690c0d06caSMauro Carvalho Chehab /* Parse all USB Video Streaming interfaces. */ 9700c0d06caSMauro Carvalho Chehab for (i = 0; i < n; ++i) { 9710c0d06caSMauro Carvalho Chehab intf = usb_ifnum_to_if(udev, buffer[12+i]); 9720c0d06caSMauro Carvalho Chehab if (intf == NULL) { 9739e56380aSJoe Perches uvc_dbg(dev, DESCR, 974ed4c5fa4SRicardo Ribalda "device %d interface %d doesn't exists\n", 9750c0d06caSMauro Carvalho Chehab udev->devnum, i); 9760c0d06caSMauro Carvalho Chehab continue; 9770c0d06caSMauro Carvalho Chehab } 9780c0d06caSMauro Carvalho Chehab 9790c0d06caSMauro Carvalho Chehab uvc_parse_streaming(dev, intf); 9800c0d06caSMauro Carvalho Chehab } 9810c0d06caSMauro Carvalho Chehab break; 9820c0d06caSMauro Carvalho Chehab 9830c0d06caSMauro Carvalho Chehab case UVC_VC_INPUT_TERMINAL: 9840c0d06caSMauro Carvalho Chehab if (buflen < 8) { 9859e56380aSJoe Perches uvc_dbg(dev, DESCR, 986ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d INPUT_TERMINAL error\n", 9870c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 9880c0d06caSMauro Carvalho Chehab return -EINVAL; 9890c0d06caSMauro Carvalho Chehab } 9900c0d06caSMauro Carvalho Chehab 99147bb1179SAlistair Strachan /* 99247bb1179SAlistair Strachan * Reject invalid terminal types that would cause issues: 99347bb1179SAlistair Strachan * 99447bb1179SAlistair Strachan * - The high byte must be non-zero, otherwise it would be 99547bb1179SAlistair Strachan * confused with a unit. 99647bb1179SAlistair Strachan * 99747bb1179SAlistair Strachan * - Bit 15 must be 0, as we use it internally as a terminal 99847bb1179SAlistair Strachan * direction flag. 99947bb1179SAlistair Strachan * 100047bb1179SAlistair Strachan * Other unknown types are accepted. 10010c0d06caSMauro Carvalho Chehab */ 10020c0d06caSMauro Carvalho Chehab type = get_unaligned_le16(&buffer[4]); 100347bb1179SAlistair Strachan if ((type & 0x7f00) == 0 || (type & 0x8000) != 0) { 10049e56380aSJoe Perches uvc_dbg(dev, DESCR, 1005ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d INPUT_TERMINAL %d has invalid type 0x%04x, skipping\n", 1006ed4c5fa4SRicardo Ribalda udev->devnum, alts->desc.bInterfaceNumber, 10070c0d06caSMauro Carvalho Chehab buffer[3], type); 10080c0d06caSMauro Carvalho Chehab return 0; 10090c0d06caSMauro Carvalho Chehab } 10100c0d06caSMauro Carvalho Chehab 10110c0d06caSMauro Carvalho Chehab n = 0; 10120c0d06caSMauro Carvalho Chehab p = 0; 10130c0d06caSMauro Carvalho Chehab len = 8; 10140c0d06caSMauro Carvalho Chehab 10150c0d06caSMauro Carvalho Chehab if (type == UVC_ITT_CAMERA) { 10160c0d06caSMauro Carvalho Chehab n = buflen >= 15 ? buffer[14] : 0; 10170c0d06caSMauro Carvalho Chehab len = 15; 10180c0d06caSMauro Carvalho Chehab 10190c0d06caSMauro Carvalho Chehab } else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) { 10200c0d06caSMauro Carvalho Chehab n = buflen >= 9 ? buffer[8] : 0; 10210c0d06caSMauro Carvalho Chehab p = buflen >= 10 + n ? buffer[9+n] : 0; 10220c0d06caSMauro Carvalho Chehab len = 10; 10230c0d06caSMauro Carvalho Chehab } 10240c0d06caSMauro Carvalho Chehab 10250c0d06caSMauro Carvalho Chehab if (buflen < len + n + p) { 10269e56380aSJoe Perches uvc_dbg(dev, DESCR, 1027ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d INPUT_TERMINAL error\n", 10280c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 10290c0d06caSMauro Carvalho Chehab return -EINVAL; 10300c0d06caSMauro Carvalho Chehab } 10310c0d06caSMauro Carvalho Chehab 1032b11813bcSThadeu Lima de Souza Cascardo term = uvc_alloc_new_entity(dev, type | UVC_TERM_INPUT, 1033b11813bcSThadeu Lima de Souza Cascardo buffer[3], 1, n + p); 1034b11813bcSThadeu Lima de Souza Cascardo if (IS_ERR(term)) 1035b11813bcSThadeu Lima de Souza Cascardo return PTR_ERR(term); 10360c0d06caSMauro Carvalho Chehab 10370c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) { 10380c0d06caSMauro Carvalho Chehab term->camera.bControlSize = n; 1039f14d4988SLaurent Pinchart term->camera.bmControls = (u8 *)term + sizeof(*term); 10400c0d06caSMauro Carvalho Chehab term->camera.wObjectiveFocalLengthMin = 10410c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[8]); 10420c0d06caSMauro Carvalho Chehab term->camera.wObjectiveFocalLengthMax = 10430c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[10]); 10440c0d06caSMauro Carvalho Chehab term->camera.wOcularFocalLength = 10450c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[12]); 10460c0d06caSMauro Carvalho Chehab memcpy(term->camera.bmControls, &buffer[15], n); 10470c0d06caSMauro Carvalho Chehab } else if (UVC_ENTITY_TYPE(term) == 10480c0d06caSMauro Carvalho Chehab UVC_ITT_MEDIA_TRANSPORT_INPUT) { 10490c0d06caSMauro Carvalho Chehab term->media.bControlSize = n; 1050f14d4988SLaurent Pinchart term->media.bmControls = (u8 *)term + sizeof(*term); 10510c0d06caSMauro Carvalho Chehab term->media.bTransportModeSize = p; 10522c6b222cSLaurent Pinchart term->media.bmTransportModes = (u8 *)term 1053f14d4988SLaurent Pinchart + sizeof(*term) + n; 10540c0d06caSMauro Carvalho Chehab memcpy(term->media.bmControls, &buffer[9], n); 10550c0d06caSMauro Carvalho Chehab memcpy(term->media.bmTransportModes, &buffer[10+n], p); 10560c0d06caSMauro Carvalho Chehab } 10570c0d06caSMauro Carvalho Chehab 10584867bb59SGuenter Roeck if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) 10595f0e659dSLaurent Pinchart type_name = "Camera"; 10605f0e659dSLaurent Pinchart else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT) 10615f0e659dSLaurent Pinchart type_name = "Media"; 10620c0d06caSMauro Carvalho Chehab else 10635f0e659dSLaurent Pinchart type_name = "Input"; 10645f0e659dSLaurent Pinchart 10655f0e659dSLaurent Pinchart uvc_entity_set_name(dev, term, type_name, buffer[7]); 10660c0d06caSMauro Carvalho Chehab 10670c0d06caSMauro Carvalho Chehab list_add_tail(&term->list, &dev->entities); 10680c0d06caSMauro Carvalho Chehab break; 10690c0d06caSMauro Carvalho Chehab 10700c0d06caSMauro Carvalho Chehab case UVC_VC_OUTPUT_TERMINAL: 10710c0d06caSMauro Carvalho Chehab if (buflen < 9) { 10729e56380aSJoe Perches uvc_dbg(dev, DESCR, 1073ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d OUTPUT_TERMINAL error\n", 10740c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 10750c0d06caSMauro Carvalho Chehab return -EINVAL; 10760c0d06caSMauro Carvalho Chehab } 10770c0d06caSMauro Carvalho Chehab 1078699b9a86SLaurent Pinchart /* 1079699b9a86SLaurent Pinchart * Make sure the terminal type MSB is not null, otherwise it 10800c0d06caSMauro Carvalho Chehab * could be confused with a unit. 10810c0d06caSMauro Carvalho Chehab */ 10820c0d06caSMauro Carvalho Chehab type = get_unaligned_le16(&buffer[4]); 10830c0d06caSMauro Carvalho Chehab if ((type & 0xff00) == 0) { 10849e56380aSJoe Perches uvc_dbg(dev, DESCR, 1085ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d OUTPUT_TERMINAL %d has invalid type 0x%04x, skipping\n", 1086ed4c5fa4SRicardo Ribalda udev->devnum, alts->desc.bInterfaceNumber, 1087ed4c5fa4SRicardo Ribalda buffer[3], type); 10880c0d06caSMauro Carvalho Chehab return 0; 10890c0d06caSMauro Carvalho Chehab } 10900c0d06caSMauro Carvalho Chehab 1091b11813bcSThadeu Lima de Souza Cascardo term = uvc_alloc_new_entity(dev, type | UVC_TERM_OUTPUT, 1092b11813bcSThadeu Lima de Souza Cascardo buffer[3], 1, 0); 1093b11813bcSThadeu Lima de Souza Cascardo if (IS_ERR(term)) 1094b11813bcSThadeu Lima de Souza Cascardo return PTR_ERR(term); 10950c0d06caSMauro Carvalho Chehab 10960c0d06caSMauro Carvalho Chehab memcpy(term->baSourceID, &buffer[7], 1); 10970c0d06caSMauro Carvalho Chehab 10985f0e659dSLaurent Pinchart uvc_entity_set_name(dev, term, "Output", buffer[8]); 10990c0d06caSMauro Carvalho Chehab 11000c0d06caSMauro Carvalho Chehab list_add_tail(&term->list, &dev->entities); 11010c0d06caSMauro Carvalho Chehab break; 11020c0d06caSMauro Carvalho Chehab 11030c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 11040c0d06caSMauro Carvalho Chehab p = buflen >= 5 ? buffer[4] : 0; 11050c0d06caSMauro Carvalho Chehab 11060c0d06caSMauro Carvalho Chehab if (buflen < 5 || buflen < 6 + p) { 11079e56380aSJoe Perches uvc_dbg(dev, DESCR, 1108ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d SELECTOR_UNIT error\n", 11090c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 11100c0d06caSMauro Carvalho Chehab return -EINVAL; 11110c0d06caSMauro Carvalho Chehab } 11120c0d06caSMauro Carvalho Chehab 1113b11813bcSThadeu Lima de Souza Cascardo unit = uvc_alloc_new_entity(dev, buffer[2], buffer[3], 1114b11813bcSThadeu Lima de Souza Cascardo p + 1, 0); 1115b11813bcSThadeu Lima de Souza Cascardo if (IS_ERR(unit)) 1116b11813bcSThadeu Lima de Souza Cascardo return PTR_ERR(unit); 11170c0d06caSMauro Carvalho Chehab 11180c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[5], p); 11190c0d06caSMauro Carvalho Chehab 11205f0e659dSLaurent Pinchart uvc_entity_set_name(dev, unit, "Selector", buffer[5+p]); 11210c0d06caSMauro Carvalho Chehab 11220c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 11230c0d06caSMauro Carvalho Chehab break; 11240c0d06caSMauro Carvalho Chehab 11250c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 11260c0d06caSMauro Carvalho Chehab n = buflen >= 8 ? buffer[7] : 0; 11270c0d06caSMauro Carvalho Chehab p = dev->uvc_version >= 0x0110 ? 10 : 9; 11280c0d06caSMauro Carvalho Chehab 11290c0d06caSMauro Carvalho Chehab if (buflen < p + n) { 11309e56380aSJoe Perches uvc_dbg(dev, DESCR, 1131ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d PROCESSING_UNIT error\n", 11320c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 11330c0d06caSMauro Carvalho Chehab return -EINVAL; 11340c0d06caSMauro Carvalho Chehab } 11350c0d06caSMauro Carvalho Chehab 1136b11813bcSThadeu Lima de Souza Cascardo unit = uvc_alloc_new_entity(dev, buffer[2], buffer[3], 2, n); 1137b11813bcSThadeu Lima de Souza Cascardo if (IS_ERR(unit)) 1138b11813bcSThadeu Lima de Souza Cascardo return PTR_ERR(unit); 11390c0d06caSMauro Carvalho Chehab 11400c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[4], 1); 11410c0d06caSMauro Carvalho Chehab unit->processing.wMaxMultiplier = 11420c0d06caSMauro Carvalho Chehab get_unaligned_le16(&buffer[5]); 11430c0d06caSMauro Carvalho Chehab unit->processing.bControlSize = buffer[7]; 1144f14d4988SLaurent Pinchart unit->processing.bmControls = (u8 *)unit + sizeof(*unit); 11450c0d06caSMauro Carvalho Chehab memcpy(unit->processing.bmControls, &buffer[8], n); 11460c0d06caSMauro Carvalho Chehab if (dev->uvc_version >= 0x0110) 11470c0d06caSMauro Carvalho Chehab unit->processing.bmVideoStandards = buffer[9+n]; 11480c0d06caSMauro Carvalho Chehab 11495f0e659dSLaurent Pinchart uvc_entity_set_name(dev, unit, "Processing", buffer[8+n]); 11500c0d06caSMauro Carvalho Chehab 11510c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 11520c0d06caSMauro Carvalho Chehab break; 11530c0d06caSMauro Carvalho Chehab 11540c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 11550c0d06caSMauro Carvalho Chehab p = buflen >= 22 ? buffer[21] : 0; 11560c0d06caSMauro Carvalho Chehab n = buflen >= 24 + p ? buffer[22+p] : 0; 11570c0d06caSMauro Carvalho Chehab 11580c0d06caSMauro Carvalho Chehab if (buflen < 24 + p + n) { 11599e56380aSJoe Perches uvc_dbg(dev, DESCR, 1160ed4c5fa4SRicardo Ribalda "device %d videocontrol interface %d EXTENSION_UNIT error\n", 11610c0d06caSMauro Carvalho Chehab udev->devnum, alts->desc.bInterfaceNumber); 11620c0d06caSMauro Carvalho Chehab return -EINVAL; 11630c0d06caSMauro Carvalho Chehab } 11640c0d06caSMauro Carvalho Chehab 1165b11813bcSThadeu Lima de Souza Cascardo unit = uvc_alloc_new_entity(dev, buffer[2], buffer[3], 1166b11813bcSThadeu Lima de Souza Cascardo p + 1, n); 1167b11813bcSThadeu Lima de Souza Cascardo if (IS_ERR(unit)) 1168b11813bcSThadeu Lima de Souza Cascardo return PTR_ERR(unit); 11690c0d06caSMauro Carvalho Chehab 1170351509c6SRicardo Ribalda memcpy(unit->guid, &buffer[4], 16); 11710c0d06caSMauro Carvalho Chehab unit->extension.bNumControls = buffer[20]; 11720c0d06caSMauro Carvalho Chehab memcpy(unit->baSourceID, &buffer[22], p); 11730c0d06caSMauro Carvalho Chehab unit->extension.bControlSize = buffer[22+p]; 1174f14d4988SLaurent Pinchart unit->extension.bmControls = (u8 *)unit + sizeof(*unit); 11750c0d06caSMauro Carvalho Chehab memcpy(unit->extension.bmControls, &buffer[23+p], n); 11760c0d06caSMauro Carvalho Chehab 11775f0e659dSLaurent Pinchart uvc_entity_set_name(dev, unit, "Extension", buffer[23+p+n]); 11780c0d06caSMauro Carvalho Chehab 11790c0d06caSMauro Carvalho Chehab list_add_tail(&unit->list, &dev->entities); 11800c0d06caSMauro Carvalho Chehab break; 11810c0d06caSMauro Carvalho Chehab 11820c0d06caSMauro Carvalho Chehab default: 11839e56380aSJoe Perches uvc_dbg(dev, DESCR, 1184ed4c5fa4SRicardo Ribalda "Found an unknown CS_INTERFACE descriptor (%u)\n", 1185ed4c5fa4SRicardo Ribalda buffer[2]); 11860c0d06caSMauro Carvalho Chehab break; 11870c0d06caSMauro Carvalho Chehab } 11880c0d06caSMauro Carvalho Chehab 11890c0d06caSMauro Carvalho Chehab return 0; 11900c0d06caSMauro Carvalho Chehab } 11910c0d06caSMauro Carvalho Chehab 11920c0d06caSMauro Carvalho Chehab static int uvc_parse_control(struct uvc_device *dev) 11930c0d06caSMauro Carvalho Chehab { 11940c0d06caSMauro Carvalho Chehab struct usb_host_interface *alts = dev->intf->cur_altsetting; 1195102df33eSLaurent Pinchart const unsigned char *buffer = alts->extra; 11960c0d06caSMauro Carvalho Chehab int buflen = alts->extralen; 11970c0d06caSMauro Carvalho Chehab int ret; 11980c0d06caSMauro Carvalho Chehab 1199699b9a86SLaurent Pinchart /* 1200699b9a86SLaurent Pinchart * Parse the default alternate setting only, as the UVC specification 12010c0d06caSMauro Carvalho Chehab * defines a single alternate setting, the default alternate setting 12020c0d06caSMauro Carvalho Chehab * zero. 12030c0d06caSMauro Carvalho Chehab */ 12040c0d06caSMauro Carvalho Chehab 12050c0d06caSMauro Carvalho Chehab while (buflen > 2) { 12060c0d06caSMauro Carvalho Chehab if (uvc_parse_vendor_control(dev, buffer, buflen) || 12070c0d06caSMauro Carvalho Chehab buffer[1] != USB_DT_CS_INTERFACE) 12080c0d06caSMauro Carvalho Chehab goto next_descriptor; 12090c0d06caSMauro Carvalho Chehab 12107b78a846SPedro Guilherme Siqueira Moreira ret = uvc_parse_standard_control(dev, buffer, buflen); 12117b78a846SPedro Guilherme Siqueira Moreira if (ret < 0) 12120c0d06caSMauro Carvalho Chehab return ret; 12130c0d06caSMauro Carvalho Chehab 12140c0d06caSMauro Carvalho Chehab next_descriptor: 12150c0d06caSMauro Carvalho Chehab buflen -= buffer[0]; 12160c0d06caSMauro Carvalho Chehab buffer += buffer[0]; 12170c0d06caSMauro Carvalho Chehab } 12180c0d06caSMauro Carvalho Chehab 1219699b9a86SLaurent Pinchart /* 1220699b9a86SLaurent Pinchart * Check if the optional status endpoint is present. Built-in iSight 12210c0d06caSMauro Carvalho Chehab * webcams have an interrupt endpoint but spit proprietary data that 12220c0d06caSMauro Carvalho Chehab * don't conform to the UVC status endpoint messages. Don't try to 12230c0d06caSMauro Carvalho Chehab * handle the interrupt endpoint for those cameras. 12240c0d06caSMauro Carvalho Chehab */ 12250c0d06caSMauro Carvalho Chehab if (alts->desc.bNumEndpoints == 1 && 12260c0d06caSMauro Carvalho Chehab !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) { 12270c0d06caSMauro Carvalho Chehab struct usb_host_endpoint *ep = &alts->endpoint[0]; 12280c0d06caSMauro Carvalho Chehab struct usb_endpoint_descriptor *desc = &ep->desc; 12290c0d06caSMauro Carvalho Chehab 12300c0d06caSMauro Carvalho Chehab if (usb_endpoint_is_int_in(desc) && 12310c0d06caSMauro Carvalho Chehab le16_to_cpu(desc->wMaxPacketSize) >= 8 && 12320c0d06caSMauro Carvalho Chehab desc->bInterval != 0) { 12339e56380aSJoe Perches uvc_dbg(dev, DESCR, 12349e56380aSJoe Perches "Found a Status endpoint (addr %02x)\n", 1235ed4c5fa4SRicardo Ribalda desc->bEndpointAddress); 12360c0d06caSMauro Carvalho Chehab dev->int_ep = ep; 12370c0d06caSMauro Carvalho Chehab } 12380c0d06caSMauro Carvalho Chehab } 12390c0d06caSMauro Carvalho Chehab 12400c0d06caSMauro Carvalho Chehab return 0; 12410c0d06caSMauro Carvalho Chehab } 12420c0d06caSMauro Carvalho Chehab 12432886477fSRicardo Ribalda /* ----------------------------------------------------------------------------- 12442886477fSRicardo Ribalda * Privacy GPIO 12452886477fSRicardo Ribalda */ 12462886477fSRicardo Ribalda 12472886477fSRicardo Ribalda static void uvc_gpio_event(struct uvc_device *dev) 12482886477fSRicardo Ribalda { 12492886477fSRicardo Ribalda struct uvc_entity *unit = dev->gpio_unit; 12502886477fSRicardo Ribalda struct uvc_video_chain *chain; 12512886477fSRicardo Ribalda u8 new_val; 12522886477fSRicardo Ribalda 12532886477fSRicardo Ribalda if (!unit) 12542886477fSRicardo Ribalda return; 12552886477fSRicardo Ribalda 12562886477fSRicardo Ribalda new_val = gpiod_get_value_cansleep(unit->gpio.gpio_privacy); 12572886477fSRicardo Ribalda 12582886477fSRicardo Ribalda /* GPIO entities are always on the first chain. */ 12592886477fSRicardo Ribalda chain = list_first_entry(&dev->chains, struct uvc_video_chain, list); 12602886477fSRicardo Ribalda uvc_ctrl_status_event(chain, unit->controls, &new_val); 12612886477fSRicardo Ribalda } 12622886477fSRicardo Ribalda 12632886477fSRicardo Ribalda static int uvc_gpio_get_cur(struct uvc_device *dev, struct uvc_entity *entity, 12642886477fSRicardo Ribalda u8 cs, void *data, u16 size) 12652886477fSRicardo Ribalda { 12662886477fSRicardo Ribalda if (cs != UVC_CT_PRIVACY_CONTROL || size < 1) 12672886477fSRicardo Ribalda return -EINVAL; 12682886477fSRicardo Ribalda 12692886477fSRicardo Ribalda *(u8 *)data = gpiod_get_value_cansleep(entity->gpio.gpio_privacy); 12702886477fSRicardo Ribalda 12712886477fSRicardo Ribalda return 0; 12722886477fSRicardo Ribalda } 12732886477fSRicardo Ribalda 12742886477fSRicardo Ribalda static int uvc_gpio_get_info(struct uvc_device *dev, struct uvc_entity *entity, 12752886477fSRicardo Ribalda u8 cs, u8 *caps) 12762886477fSRicardo Ribalda { 12772886477fSRicardo Ribalda if (cs != UVC_CT_PRIVACY_CONTROL) 12782886477fSRicardo Ribalda return -EINVAL; 12792886477fSRicardo Ribalda 12802886477fSRicardo Ribalda *caps = UVC_CONTROL_CAP_GET | UVC_CONTROL_CAP_AUTOUPDATE; 12812886477fSRicardo Ribalda return 0; 12822886477fSRicardo Ribalda } 12832886477fSRicardo Ribalda 12842886477fSRicardo Ribalda static irqreturn_t uvc_gpio_irq(int irq, void *data) 12852886477fSRicardo Ribalda { 12862886477fSRicardo Ribalda struct uvc_device *dev = data; 12872886477fSRicardo Ribalda 12882886477fSRicardo Ribalda uvc_gpio_event(dev); 12892886477fSRicardo Ribalda return IRQ_HANDLED; 12902886477fSRicardo Ribalda } 12912886477fSRicardo Ribalda 12922886477fSRicardo Ribalda static int uvc_gpio_parse(struct uvc_device *dev) 12932886477fSRicardo Ribalda { 12942886477fSRicardo Ribalda struct uvc_entity *unit; 12952886477fSRicardo Ribalda struct gpio_desc *gpio_privacy; 12962886477fSRicardo Ribalda int irq; 12972886477fSRicardo Ribalda 12982886477fSRicardo Ribalda gpio_privacy = devm_gpiod_get_optional(&dev->udev->dev, "privacy", 12992886477fSRicardo Ribalda GPIOD_IN); 13002886477fSRicardo Ribalda if (IS_ERR_OR_NULL(gpio_privacy)) 13012886477fSRicardo Ribalda return PTR_ERR_OR_ZERO(gpio_privacy); 13022886477fSRicardo Ribalda 13032886477fSRicardo Ribalda irq = gpiod_to_irq(gpio_privacy); 13046cb7d1b3SYang Yingliang if (irq < 0) 13056cb7d1b3SYang Yingliang return dev_err_probe(&dev->udev->dev, irq, 13066cb7d1b3SYang Yingliang "No IRQ for privacy GPIO\n"); 13072886477fSRicardo Ribalda 1308b11813bcSThadeu Lima de Souza Cascardo unit = uvc_alloc_new_entity(dev, UVC_EXT_GPIO_UNIT, 1309b11813bcSThadeu Lima de Souza Cascardo UVC_EXT_GPIO_UNIT_ID, 0, 1); 1310b11813bcSThadeu Lima de Souza Cascardo if (IS_ERR(unit)) 1311b11813bcSThadeu Lima de Souza Cascardo return PTR_ERR(unit); 1312f0f07845SJosé Expósito 13132886477fSRicardo Ribalda unit->gpio.gpio_privacy = gpio_privacy; 13142886477fSRicardo Ribalda unit->gpio.irq = irq; 13152886477fSRicardo Ribalda unit->gpio.bControlSize = 1; 13162886477fSRicardo Ribalda unit->gpio.bmControls = (u8 *)unit + sizeof(*unit); 13172886477fSRicardo Ribalda unit->gpio.bmControls[0] = 1; 13182886477fSRicardo Ribalda unit->get_cur = uvc_gpio_get_cur; 13192886477fSRicardo Ribalda unit->get_info = uvc_gpio_get_info; 1320063b811fSHans Verkuil strscpy(unit->name, "GPIO", sizeof(unit->name)); 13212886477fSRicardo Ribalda 13222886477fSRicardo Ribalda list_add_tail(&unit->list, &dev->entities); 13232886477fSRicardo Ribalda 13242886477fSRicardo Ribalda dev->gpio_unit = unit; 13252886477fSRicardo Ribalda 13262886477fSRicardo Ribalda return 0; 13272886477fSRicardo Ribalda } 13282886477fSRicardo Ribalda 13292886477fSRicardo Ribalda static int uvc_gpio_init_irq(struct uvc_device *dev) 13302886477fSRicardo Ribalda { 13312886477fSRicardo Ribalda struct uvc_entity *unit = dev->gpio_unit; 13322886477fSRicardo Ribalda 13332886477fSRicardo Ribalda if (!unit || unit->gpio.irq < 0) 13342886477fSRicardo Ribalda return 0; 13352886477fSRicardo Ribalda 13362886477fSRicardo Ribalda return devm_request_threaded_irq(&dev->udev->dev, unit->gpio.irq, NULL, 13372886477fSRicardo Ribalda uvc_gpio_irq, 13382886477fSRicardo Ribalda IRQF_ONESHOT | IRQF_TRIGGER_FALLING | 13392886477fSRicardo Ribalda IRQF_TRIGGER_RISING, 13402886477fSRicardo Ribalda "uvc_privacy_gpio", dev); 13412886477fSRicardo Ribalda } 13422886477fSRicardo Ribalda 13430c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 13440c0d06caSMauro Carvalho Chehab * UVC device scan 13450c0d06caSMauro Carvalho Chehab */ 13460c0d06caSMauro Carvalho Chehab 13470c0d06caSMauro Carvalho Chehab /* 13480c0d06caSMauro Carvalho Chehab * Scan the UVC descriptors to locate a chain starting at an Output Terminal 13490c0d06caSMauro Carvalho Chehab * and containing the following units: 13500c0d06caSMauro Carvalho Chehab * 13510c0d06caSMauro Carvalho Chehab * - one or more Output Terminals (USB Streaming or Display) 13520c0d06caSMauro Carvalho Chehab * - zero or one Processing Unit 13530c0d06caSMauro Carvalho Chehab * - zero, one or more single-input Selector Units 13540c0d06caSMauro Carvalho Chehab * - zero or one multiple-input Selector Units, provided all inputs are 13550c0d06caSMauro Carvalho Chehab * connected to input terminals 13560c0d06caSMauro Carvalho Chehab * - zero, one or mode single-input Extension Units 13570c0d06caSMauro Carvalho Chehab * - one or more Input Terminals (Camera, External or USB Streaming) 13580c0d06caSMauro Carvalho Chehab * 13590c0d06caSMauro Carvalho Chehab * The terminal and units must match on of the following structures: 13600c0d06caSMauro Carvalho Chehab * 13610c0d06caSMauro Carvalho Chehab * ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0) 13620c0d06caSMauro Carvalho Chehab * ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ... 13630c0d06caSMauro Carvalho Chehab * ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n) 13640c0d06caSMauro Carvalho Chehab * 13650c0d06caSMauro Carvalho Chehab * +---------+ +---------+ -> OTT_*(0) 13660c0d06caSMauro Carvalho Chehab * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ... 13670c0d06caSMauro Carvalho Chehab * +---------+ +---------+ -> OTT_*(n) 13680c0d06caSMauro Carvalho Chehab * 13690c0d06caSMauro Carvalho Chehab * The Processing Unit and Extension Units can be in any order. Additional 13700c0d06caSMauro Carvalho Chehab * Extension Units connected to the main chain as single-unit branches are 13710c0d06caSMauro Carvalho Chehab * also supported. Single-input Selector Units are ignored. 13720c0d06caSMauro Carvalho Chehab */ 13730c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_entity(struct uvc_video_chain *chain, 13740c0d06caSMauro Carvalho Chehab struct uvc_entity *entity) 13750c0d06caSMauro Carvalho Chehab { 13760c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(entity)) { 13770c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 13789e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- XU %d", entity->id); 13790c0d06caSMauro Carvalho Chehab 13800c0d06caSMauro Carvalho Chehab if (entity->bNrInPins != 1) { 13819e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 13829e56380aSJoe Perches "Extension unit %d has more than 1 input pin\n", 1383ed4c5fa4SRicardo Ribalda entity->id); 13840c0d06caSMauro Carvalho Chehab return -1; 13850c0d06caSMauro Carvalho Chehab } 13860c0d06caSMauro Carvalho Chehab 13870c0d06caSMauro Carvalho Chehab break; 13880c0d06caSMauro Carvalho Chehab 13890c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 13909e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- PU %d", entity->id); 13910c0d06caSMauro Carvalho Chehab 13920c0d06caSMauro Carvalho Chehab if (chain->processing != NULL) { 13939e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 13949e56380aSJoe Perches "Found multiple Processing Units in chain\n"); 13950c0d06caSMauro Carvalho Chehab return -1; 13960c0d06caSMauro Carvalho Chehab } 13970c0d06caSMauro Carvalho Chehab 13980c0d06caSMauro Carvalho Chehab chain->processing = entity; 13990c0d06caSMauro Carvalho Chehab break; 14000c0d06caSMauro Carvalho Chehab 14010c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 14029e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- SU %d", entity->id); 14030c0d06caSMauro Carvalho Chehab 14040c0d06caSMauro Carvalho Chehab /* Single-input selector units are ignored. */ 14050c0d06caSMauro Carvalho Chehab if (entity->bNrInPins == 1) 14060c0d06caSMauro Carvalho Chehab break; 14070c0d06caSMauro Carvalho Chehab 14080c0d06caSMauro Carvalho Chehab if (chain->selector != NULL) { 14099e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 14109e56380aSJoe Perches "Found multiple Selector Units in chain\n"); 14110c0d06caSMauro Carvalho Chehab return -1; 14120c0d06caSMauro Carvalho Chehab } 14130c0d06caSMauro Carvalho Chehab 14140c0d06caSMauro Carvalho Chehab chain->selector = entity; 14150c0d06caSMauro Carvalho Chehab break; 14160c0d06caSMauro Carvalho Chehab 14170c0d06caSMauro Carvalho Chehab case UVC_ITT_VENDOR_SPECIFIC: 14180c0d06caSMauro Carvalho Chehab case UVC_ITT_CAMERA: 14190c0d06caSMauro Carvalho Chehab case UVC_ITT_MEDIA_TRANSPORT_INPUT: 14209e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- IT %d\n", entity->id); 14210c0d06caSMauro Carvalho Chehab 14220c0d06caSMauro Carvalho Chehab break; 14230c0d06caSMauro Carvalho Chehab 14240c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 14250c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 14260c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 14279e56380aSJoe Perches uvc_dbg_cont(PROBE, " OT %d", entity->id); 14280c0d06caSMauro Carvalho Chehab 14290c0d06caSMauro Carvalho Chehab break; 14300c0d06caSMauro Carvalho Chehab 14310c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 143259e92bf6SRicardo Ribalda if (UVC_ENTITY_IS_ITERM(entity)) 14339e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- IT %d\n", entity->id); 143459e92bf6SRicardo Ribalda else 14359e56380aSJoe Perches uvc_dbg_cont(PROBE, " OT %d", entity->id); 14360c0d06caSMauro Carvalho Chehab 14370c0d06caSMauro Carvalho Chehab break; 14380c0d06caSMauro Carvalho Chehab 14390c0d06caSMauro Carvalho Chehab default: 14409e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 14419e56380aSJoe Perches "Unsupported entity type 0x%04x found in chain\n", 1442ed4c5fa4SRicardo Ribalda UVC_ENTITY_TYPE(entity)); 14430c0d06caSMauro Carvalho Chehab return -1; 14440c0d06caSMauro Carvalho Chehab } 14450c0d06caSMauro Carvalho Chehab 14460c0d06caSMauro Carvalho Chehab list_add_tail(&entity->chain, &chain->entities); 14470c0d06caSMauro Carvalho Chehab return 0; 14480c0d06caSMauro Carvalho Chehab } 14490c0d06caSMauro Carvalho Chehab 14500c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_forward(struct uvc_video_chain *chain, 14510c0d06caSMauro Carvalho Chehab struct uvc_entity *entity, struct uvc_entity *prev) 14520c0d06caSMauro Carvalho Chehab { 14530c0d06caSMauro Carvalho Chehab struct uvc_entity *forward; 14540c0d06caSMauro Carvalho Chehab int found; 14550c0d06caSMauro Carvalho Chehab 14560c0d06caSMauro Carvalho Chehab /* Forward scan */ 14570c0d06caSMauro Carvalho Chehab forward = NULL; 14580c0d06caSMauro Carvalho Chehab found = 0; 14590c0d06caSMauro Carvalho Chehab 14600c0d06caSMauro Carvalho Chehab while (1) { 14610c0d06caSMauro Carvalho Chehab forward = uvc_entity_by_reference(chain->dev, entity->id, 14620c0d06caSMauro Carvalho Chehab forward); 14630c0d06caSMauro Carvalho Chehab if (forward == NULL) 14640c0d06caSMauro Carvalho Chehab break; 14650c0d06caSMauro Carvalho Chehab if (forward == prev) 14660c0d06caSMauro Carvalho Chehab continue; 146768035c80SWill Deacon if (forward->chain.next || forward->chain.prev) { 14689e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 14699e56380aSJoe Perches "Found reference to entity %d already in chain\n", 1470ed4c5fa4SRicardo Ribalda forward->id); 147168035c80SWill Deacon return -EINVAL; 147268035c80SWill Deacon } 14730c0d06caSMauro Carvalho Chehab 14740c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(forward)) { 14750c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 14760c0d06caSMauro Carvalho Chehab if (forward->bNrInPins != 1) { 14779e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 14789e56380aSJoe Perches "Extension unit %d has more than 1 input pin\n", 147932934486SLaurent Pinchart forward->id); 14800c0d06caSMauro Carvalho Chehab return -EINVAL; 14810c0d06caSMauro Carvalho Chehab } 14820c0d06caSMauro Carvalho Chehab 14834ca052b4SLaurent Pinchart /* 14844ca052b4SLaurent Pinchart * Some devices reference an output terminal as the 14854ca052b4SLaurent Pinchart * source of extension units. This is incorrect, as 14864ca052b4SLaurent Pinchart * output terminals only have an input pin, and thus 14874ca052b4SLaurent Pinchart * can't be connected to any entity in the forward 14884ca052b4SLaurent Pinchart * direction. The resulting topology would cause issues 14894ca052b4SLaurent Pinchart * when registering the media controller graph. To 14904ca052b4SLaurent Pinchart * avoid this problem, connect the extension unit to 14914ca052b4SLaurent Pinchart * the source of the output terminal instead. 14924ca052b4SLaurent Pinchart */ 14934ca052b4SLaurent Pinchart if (UVC_ENTITY_IS_OTERM(entity)) { 14944ca052b4SLaurent Pinchart struct uvc_entity *source; 14954ca052b4SLaurent Pinchart 14964ca052b4SLaurent Pinchart source = uvc_entity_by_id(chain->dev, 14974ca052b4SLaurent Pinchart entity->baSourceID[0]); 14984ca052b4SLaurent Pinchart if (!source) { 14994ca052b4SLaurent Pinchart uvc_dbg(chain->dev, DESCR, 15004ca052b4SLaurent Pinchart "Can't connect extension unit %u in chain\n", 15014ca052b4SLaurent Pinchart forward->id); 15024ca052b4SLaurent Pinchart break; 15034ca052b4SLaurent Pinchart } 15044ca052b4SLaurent Pinchart 15054ca052b4SLaurent Pinchart forward->baSourceID[0] = source->id; 15064ca052b4SLaurent Pinchart } 15074ca052b4SLaurent Pinchart 15080c0d06caSMauro Carvalho Chehab list_add_tail(&forward->chain, &chain->entities); 15090c0d06caSMauro Carvalho Chehab if (!found) 15109e56380aSJoe Perches uvc_dbg_cont(PROBE, " (->"); 15110c0d06caSMauro Carvalho Chehab 15129e56380aSJoe Perches uvc_dbg_cont(PROBE, " XU %d", forward->id); 15130c0d06caSMauro Carvalho Chehab found = 1; 15140c0d06caSMauro Carvalho Chehab break; 15150c0d06caSMauro Carvalho Chehab 15160c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 15170c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 15180c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 15190c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 15200c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_IS_ITERM(forward)) { 15219e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 15229e56380aSJoe Perches "Unsupported input terminal %u\n", 1523ed4c5fa4SRicardo Ribalda forward->id); 15240c0d06caSMauro Carvalho Chehab return -EINVAL; 15250c0d06caSMauro Carvalho Chehab } 15260c0d06caSMauro Carvalho Chehab 15274ca052b4SLaurent Pinchart if (UVC_ENTITY_IS_OTERM(entity)) { 15284ca052b4SLaurent Pinchart uvc_dbg(chain->dev, DESCR, 15294ca052b4SLaurent Pinchart "Unsupported connection between output terminals %u and %u\n", 15304ca052b4SLaurent Pinchart entity->id, forward->id); 15314ca052b4SLaurent Pinchart break; 15324ca052b4SLaurent Pinchart } 15334ca052b4SLaurent Pinchart 15340c0d06caSMauro Carvalho Chehab list_add_tail(&forward->chain, &chain->entities); 15350c0d06caSMauro Carvalho Chehab if (!found) 15369e56380aSJoe Perches uvc_dbg_cont(PROBE, " (->"); 15370c0d06caSMauro Carvalho Chehab 15389e56380aSJoe Perches uvc_dbg_cont(PROBE, " OT %d", forward->id); 15390c0d06caSMauro Carvalho Chehab found = 1; 15400c0d06caSMauro Carvalho Chehab break; 15410c0d06caSMauro Carvalho Chehab } 15420c0d06caSMauro Carvalho Chehab } 15430c0d06caSMauro Carvalho Chehab if (found) 15449e56380aSJoe Perches uvc_dbg_cont(PROBE, ")"); 15450c0d06caSMauro Carvalho Chehab 15460c0d06caSMauro Carvalho Chehab return 0; 15470c0d06caSMauro Carvalho Chehab } 15480c0d06caSMauro Carvalho Chehab 15490c0d06caSMauro Carvalho Chehab static int uvc_scan_chain_backward(struct uvc_video_chain *chain, 15500c0d06caSMauro Carvalho Chehab struct uvc_entity **_entity) 15510c0d06caSMauro Carvalho Chehab { 15520c0d06caSMauro Carvalho Chehab struct uvc_entity *entity = *_entity; 15530c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 15540c0d06caSMauro Carvalho Chehab int id = -EINVAL, i; 15550c0d06caSMauro Carvalho Chehab 15560c0d06caSMauro Carvalho Chehab switch (UVC_ENTITY_TYPE(entity)) { 15570c0d06caSMauro Carvalho Chehab case UVC_VC_EXTENSION_UNIT: 15580c0d06caSMauro Carvalho Chehab case UVC_VC_PROCESSING_UNIT: 15590c0d06caSMauro Carvalho Chehab id = entity->baSourceID[0]; 15600c0d06caSMauro Carvalho Chehab break; 15610c0d06caSMauro Carvalho Chehab 15620c0d06caSMauro Carvalho Chehab case UVC_VC_SELECTOR_UNIT: 15630c0d06caSMauro Carvalho Chehab /* Single-input selector units are ignored. */ 15640c0d06caSMauro Carvalho Chehab if (entity->bNrInPins == 1) { 15650c0d06caSMauro Carvalho Chehab id = entity->baSourceID[0]; 15660c0d06caSMauro Carvalho Chehab break; 15670c0d06caSMauro Carvalho Chehab } 15680c0d06caSMauro Carvalho Chehab 15699e56380aSJoe Perches uvc_dbg_cont(PROBE, " <- IT"); 15700c0d06caSMauro Carvalho Chehab 15710c0d06caSMauro Carvalho Chehab chain->selector = entity; 15720c0d06caSMauro Carvalho Chehab for (i = 0; i < entity->bNrInPins; ++i) { 15730c0d06caSMauro Carvalho Chehab id = entity->baSourceID[i]; 15740c0d06caSMauro Carvalho Chehab term = uvc_entity_by_id(chain->dev, id); 15750c0d06caSMauro Carvalho Chehab if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { 15769e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 1577ed4c5fa4SRicardo Ribalda "Selector unit %d input %d isn't connected to an input terminal\n", 1578ed4c5fa4SRicardo Ribalda entity->id, i); 15790c0d06caSMauro Carvalho Chehab return -1; 15800c0d06caSMauro Carvalho Chehab } 15810c0d06caSMauro Carvalho Chehab 158268035c80SWill Deacon if (term->chain.next || term->chain.prev) { 15839e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 15849e56380aSJoe Perches "Found reference to entity %d already in chain\n", 158568035c80SWill Deacon term->id); 158668035c80SWill Deacon return -EINVAL; 158768035c80SWill Deacon } 158868035c80SWill Deacon 15899e56380aSJoe Perches uvc_dbg_cont(PROBE, " %d", term->id); 15900c0d06caSMauro Carvalho Chehab 15910c0d06caSMauro Carvalho Chehab list_add_tail(&term->chain, &chain->entities); 15920c0d06caSMauro Carvalho Chehab uvc_scan_chain_forward(chain, term, entity); 15930c0d06caSMauro Carvalho Chehab } 15940c0d06caSMauro Carvalho Chehab 15959e56380aSJoe Perches uvc_dbg_cont(PROBE, "\n"); 15960c0d06caSMauro Carvalho Chehab 15970c0d06caSMauro Carvalho Chehab id = 0; 15980c0d06caSMauro Carvalho Chehab break; 15990c0d06caSMauro Carvalho Chehab 16000c0d06caSMauro Carvalho Chehab case UVC_ITT_VENDOR_SPECIFIC: 16010c0d06caSMauro Carvalho Chehab case UVC_ITT_CAMERA: 16020c0d06caSMauro Carvalho Chehab case UVC_ITT_MEDIA_TRANSPORT_INPUT: 16030c0d06caSMauro Carvalho Chehab case UVC_OTT_VENDOR_SPECIFIC: 16040c0d06caSMauro Carvalho Chehab case UVC_OTT_DISPLAY: 16050c0d06caSMauro Carvalho Chehab case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 16060c0d06caSMauro Carvalho Chehab case UVC_TT_STREAMING: 16070c0d06caSMauro Carvalho Chehab id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0; 16080c0d06caSMauro Carvalho Chehab break; 16090c0d06caSMauro Carvalho Chehab } 16100c0d06caSMauro Carvalho Chehab 16110c0d06caSMauro Carvalho Chehab if (id <= 0) { 16120c0d06caSMauro Carvalho Chehab *_entity = NULL; 16130c0d06caSMauro Carvalho Chehab return id; 16140c0d06caSMauro Carvalho Chehab } 16150c0d06caSMauro Carvalho Chehab 16160c0d06caSMauro Carvalho Chehab entity = uvc_entity_by_id(chain->dev, id); 16170c0d06caSMauro Carvalho Chehab if (entity == NULL) { 16189e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 16199e56380aSJoe Perches "Found reference to unknown entity %d\n", id); 16200c0d06caSMauro Carvalho Chehab return -EINVAL; 16210c0d06caSMauro Carvalho Chehab } 16220c0d06caSMauro Carvalho Chehab 16230c0d06caSMauro Carvalho Chehab *_entity = entity; 16240c0d06caSMauro Carvalho Chehab return 0; 16250c0d06caSMauro Carvalho Chehab } 16260c0d06caSMauro Carvalho Chehab 16270c0d06caSMauro Carvalho Chehab static int uvc_scan_chain(struct uvc_video_chain *chain, 16280c0d06caSMauro Carvalho Chehab struct uvc_entity *term) 16290c0d06caSMauro Carvalho Chehab { 16300c0d06caSMauro Carvalho Chehab struct uvc_entity *entity, *prev; 16310c0d06caSMauro Carvalho Chehab 16329e56380aSJoe Perches uvc_dbg(chain->dev, PROBE, "Scanning UVC chain:"); 16330c0d06caSMauro Carvalho Chehab 16340c0d06caSMauro Carvalho Chehab entity = term; 16350c0d06caSMauro Carvalho Chehab prev = NULL; 16360c0d06caSMauro Carvalho Chehab 16370c0d06caSMauro Carvalho Chehab while (entity != NULL) { 16380c0d06caSMauro Carvalho Chehab /* Entity must not be part of an existing chain */ 16390c0d06caSMauro Carvalho Chehab if (entity->chain.next || entity->chain.prev) { 16409e56380aSJoe Perches uvc_dbg(chain->dev, DESCR, 16419e56380aSJoe Perches "Found reference to entity %d already in chain\n", 1642ed4c5fa4SRicardo Ribalda entity->id); 16430c0d06caSMauro Carvalho Chehab return -EINVAL; 16440c0d06caSMauro Carvalho Chehab } 16450c0d06caSMauro Carvalho Chehab 16460c0d06caSMauro Carvalho Chehab /* Process entity */ 16470c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_entity(chain, entity) < 0) 16480c0d06caSMauro Carvalho Chehab return -EINVAL; 16490c0d06caSMauro Carvalho Chehab 16500c0d06caSMauro Carvalho Chehab /* Forward scan */ 16510c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_forward(chain, entity, prev) < 0) 16520c0d06caSMauro Carvalho Chehab return -EINVAL; 16530c0d06caSMauro Carvalho Chehab 16540c0d06caSMauro Carvalho Chehab /* Backward scan */ 16550c0d06caSMauro Carvalho Chehab prev = entity; 16560c0d06caSMauro Carvalho Chehab if (uvc_scan_chain_backward(chain, &entity) < 0) 16570c0d06caSMauro Carvalho Chehab return -EINVAL; 16580c0d06caSMauro Carvalho Chehab } 16590c0d06caSMauro Carvalho Chehab 16600c0d06caSMauro Carvalho Chehab return 0; 16610c0d06caSMauro Carvalho Chehab } 16620c0d06caSMauro Carvalho Chehab 16630c0d06caSMauro Carvalho Chehab static unsigned int uvc_print_terms(struct list_head *terms, u16 dir, 16640c0d06caSMauro Carvalho Chehab char *buffer) 16650c0d06caSMauro Carvalho Chehab { 16660c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 16670c0d06caSMauro Carvalho Chehab unsigned int nterms = 0; 16680c0d06caSMauro Carvalho Chehab char *p = buffer; 16690c0d06caSMauro Carvalho Chehab 16700c0d06caSMauro Carvalho Chehab list_for_each_entry(term, terms, chain) { 16710c0d06caSMauro Carvalho Chehab if (!UVC_ENTITY_IS_TERM(term) || 16720c0d06caSMauro Carvalho Chehab UVC_TERM_DIRECTION(term) != dir) 16730c0d06caSMauro Carvalho Chehab continue; 16740c0d06caSMauro Carvalho Chehab 16750c0d06caSMauro Carvalho Chehab if (nterms) 16760c0d06caSMauro Carvalho Chehab p += sprintf(p, ","); 16770c0d06caSMauro Carvalho Chehab if (++nterms >= 4) { 16780c0d06caSMauro Carvalho Chehab p += sprintf(p, "..."); 16790c0d06caSMauro Carvalho Chehab break; 16800c0d06caSMauro Carvalho Chehab } 16810c0d06caSMauro Carvalho Chehab p += sprintf(p, "%u", term->id); 16820c0d06caSMauro Carvalho Chehab } 16830c0d06caSMauro Carvalho Chehab 16840c0d06caSMauro Carvalho Chehab return p - buffer; 16850c0d06caSMauro Carvalho Chehab } 16860c0d06caSMauro Carvalho Chehab 16870c0d06caSMauro Carvalho Chehab static const char *uvc_print_chain(struct uvc_video_chain *chain) 16880c0d06caSMauro Carvalho Chehab { 16890c0d06caSMauro Carvalho Chehab static char buffer[43]; 16900c0d06caSMauro Carvalho Chehab char *p = buffer; 16910c0d06caSMauro Carvalho Chehab 16920c0d06caSMauro Carvalho Chehab p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p); 16930c0d06caSMauro Carvalho Chehab p += sprintf(p, " -> "); 16940c0d06caSMauro Carvalho Chehab uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p); 16950c0d06caSMauro Carvalho Chehab 16960c0d06caSMauro Carvalho Chehab return buffer; 16970c0d06caSMauro Carvalho Chehab } 16980c0d06caSMauro Carvalho Chehab 1699e950267aSHenrik Ingo static struct uvc_video_chain *uvc_alloc_chain(struct uvc_device *dev) 1700e950267aSHenrik Ingo { 1701e950267aSHenrik Ingo struct uvc_video_chain *chain; 1702e950267aSHenrik Ingo 1703e950267aSHenrik Ingo chain = kzalloc(sizeof(*chain), GFP_KERNEL); 1704e950267aSHenrik Ingo if (chain == NULL) 1705e950267aSHenrik Ingo return NULL; 1706e950267aSHenrik Ingo 1707e950267aSHenrik Ingo INIT_LIST_HEAD(&chain->entities); 1708e950267aSHenrik Ingo mutex_init(&chain->ctrl_mutex); 1709e950267aSHenrik Ingo chain->dev = dev; 1710e950267aSHenrik Ingo v4l2_prio_init(&chain->prio); 1711e950267aSHenrik Ingo 1712e950267aSHenrik Ingo return chain; 1713e950267aSHenrik Ingo } 1714e950267aSHenrik Ingo 1715e950267aSHenrik Ingo /* 1716e950267aSHenrik Ingo * Fallback heuristic for devices that don't connect units and terminals in a 1717e950267aSHenrik Ingo * valid chain. 1718e950267aSHenrik Ingo * 1719e950267aSHenrik Ingo * Some devices have invalid baSourceID references, causing uvc_scan_chain() 1720e950267aSHenrik Ingo * to fail, but if we just take the entities we can find and put them together 1721e950267aSHenrik Ingo * in the most sensible chain we can think of, turns out they do work anyway. 1722e950267aSHenrik Ingo * Note: This heuristic assumes there is a single chain. 1723e950267aSHenrik Ingo * 1724e950267aSHenrik Ingo * At the time of writing, devices known to have such a broken chain are 1725e950267aSHenrik Ingo * - Acer Integrated Camera (5986:055a) 1726e950267aSHenrik Ingo * - Realtek rtl157a7 (0bda:57a7) 1727e950267aSHenrik Ingo */ 1728e950267aSHenrik Ingo static int uvc_scan_fallback(struct uvc_device *dev) 1729e950267aSHenrik Ingo { 1730e950267aSHenrik Ingo struct uvc_video_chain *chain; 1731e950267aSHenrik Ingo struct uvc_entity *iterm = NULL; 1732e950267aSHenrik Ingo struct uvc_entity *oterm = NULL; 1733e950267aSHenrik Ingo struct uvc_entity *entity; 1734e950267aSHenrik Ingo struct uvc_entity *prev; 1735e950267aSHenrik Ingo 1736e950267aSHenrik Ingo /* 1737e950267aSHenrik Ingo * Start by locating the input and output terminals. We only support 1738e950267aSHenrik Ingo * devices with exactly one of each for now. 1739e950267aSHenrik Ingo */ 1740e950267aSHenrik Ingo list_for_each_entry(entity, &dev->entities, list) { 1741e950267aSHenrik Ingo if (UVC_ENTITY_IS_ITERM(entity)) { 1742e950267aSHenrik Ingo if (iterm) 1743e950267aSHenrik Ingo return -EINVAL; 1744e950267aSHenrik Ingo iterm = entity; 1745e950267aSHenrik Ingo } 1746e950267aSHenrik Ingo 1747e950267aSHenrik Ingo if (UVC_ENTITY_IS_OTERM(entity)) { 1748e950267aSHenrik Ingo if (oterm) 1749e950267aSHenrik Ingo return -EINVAL; 1750e950267aSHenrik Ingo oterm = entity; 1751e950267aSHenrik Ingo } 1752e950267aSHenrik Ingo } 1753e950267aSHenrik Ingo 1754e950267aSHenrik Ingo if (iterm == NULL || oterm == NULL) 1755e950267aSHenrik Ingo return -EINVAL; 1756e950267aSHenrik Ingo 1757e950267aSHenrik Ingo /* Allocate the chain and fill it. */ 1758e950267aSHenrik Ingo chain = uvc_alloc_chain(dev); 1759e950267aSHenrik Ingo if (chain == NULL) 1760e950267aSHenrik Ingo return -ENOMEM; 1761e950267aSHenrik Ingo 1762e950267aSHenrik Ingo if (uvc_scan_chain_entity(chain, oterm) < 0) 1763e950267aSHenrik Ingo goto error; 1764e950267aSHenrik Ingo 1765e950267aSHenrik Ingo prev = oterm; 1766e950267aSHenrik Ingo 1767e950267aSHenrik Ingo /* 1768e950267aSHenrik Ingo * Add all Processing and Extension Units with two pads. The order 1769e950267aSHenrik Ingo * doesn't matter much, use reverse list traversal to connect units in 1770e950267aSHenrik Ingo * UVC descriptor order as we build the chain from output to input. This 1771e950267aSHenrik Ingo * leads to units appearing in the order meant by the manufacturer for 1772e950267aSHenrik Ingo * the cameras known to require this heuristic. 1773e950267aSHenrik Ingo */ 1774e950267aSHenrik Ingo list_for_each_entry_reverse(entity, &dev->entities, list) { 1775e950267aSHenrik Ingo if (entity->type != UVC_VC_PROCESSING_UNIT && 1776e950267aSHenrik Ingo entity->type != UVC_VC_EXTENSION_UNIT) 1777e950267aSHenrik Ingo continue; 1778e950267aSHenrik Ingo 1779e950267aSHenrik Ingo if (entity->num_pads != 2) 1780e950267aSHenrik Ingo continue; 1781e950267aSHenrik Ingo 1782e950267aSHenrik Ingo if (uvc_scan_chain_entity(chain, entity) < 0) 1783e950267aSHenrik Ingo goto error; 1784e950267aSHenrik Ingo 1785e950267aSHenrik Ingo prev->baSourceID[0] = entity->id; 1786e950267aSHenrik Ingo prev = entity; 1787e950267aSHenrik Ingo } 1788e950267aSHenrik Ingo 1789e950267aSHenrik Ingo if (uvc_scan_chain_entity(chain, iterm) < 0) 1790e950267aSHenrik Ingo goto error; 1791e950267aSHenrik Ingo 1792e950267aSHenrik Ingo prev->baSourceID[0] = iterm->id; 1793e950267aSHenrik Ingo 1794e950267aSHenrik Ingo list_add_tail(&chain->list, &dev->chains); 1795e950267aSHenrik Ingo 17969e56380aSJoe Perches uvc_dbg(dev, PROBE, "Found a video chain by fallback heuristic (%s)\n", 1797e950267aSHenrik Ingo uvc_print_chain(chain)); 1798e950267aSHenrik Ingo 1799e950267aSHenrik Ingo return 0; 1800e950267aSHenrik Ingo 1801e950267aSHenrik Ingo error: 1802e950267aSHenrik Ingo kfree(chain); 1803e950267aSHenrik Ingo return -EINVAL; 1804e950267aSHenrik Ingo } 1805e950267aSHenrik Ingo 18060c0d06caSMauro Carvalho Chehab /* 18070c0d06caSMauro Carvalho Chehab * Scan the device for video chains and register video devices. 18080c0d06caSMauro Carvalho Chehab * 18090c0d06caSMauro Carvalho Chehab * Chains are scanned starting at their output terminals and walked backwards. 18100c0d06caSMauro Carvalho Chehab */ 18110c0d06caSMauro Carvalho Chehab static int uvc_scan_device(struct uvc_device *dev) 18120c0d06caSMauro Carvalho Chehab { 18130c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 18140c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 18150c0d06caSMauro Carvalho Chehab 18160c0d06caSMauro Carvalho Chehab list_for_each_entry(term, &dev->entities, list) { 18170c0d06caSMauro Carvalho Chehab if (!UVC_ENTITY_IS_OTERM(term)) 18180c0d06caSMauro Carvalho Chehab continue; 18190c0d06caSMauro Carvalho Chehab 1820699b9a86SLaurent Pinchart /* 1821699b9a86SLaurent Pinchart * If the terminal is already included in a chain, skip it. 18220c0d06caSMauro Carvalho Chehab * This can happen for chains that have multiple output 18230c0d06caSMauro Carvalho Chehab * terminals, where all output terminals beside the first one 18240c0d06caSMauro Carvalho Chehab * will be inserted in the chain in forward scans. 18250c0d06caSMauro Carvalho Chehab */ 18260c0d06caSMauro Carvalho Chehab if (term->chain.next || term->chain.prev) 18270c0d06caSMauro Carvalho Chehab continue; 18280c0d06caSMauro Carvalho Chehab 1829e950267aSHenrik Ingo chain = uvc_alloc_chain(dev); 18300c0d06caSMauro Carvalho Chehab if (chain == NULL) 18310c0d06caSMauro Carvalho Chehab return -ENOMEM; 18320c0d06caSMauro Carvalho Chehab 18338be8ec6eSLaurent Pinchart term->flags |= UVC_ENTITY_FLAG_DEFAULT; 18348be8ec6eSLaurent Pinchart 18350c0d06caSMauro Carvalho Chehab if (uvc_scan_chain(chain, term) < 0) { 18360c0d06caSMauro Carvalho Chehab kfree(chain); 18370c0d06caSMauro Carvalho Chehab continue; 18380c0d06caSMauro Carvalho Chehab } 18390c0d06caSMauro Carvalho Chehab 18409e56380aSJoe Perches uvc_dbg(dev, PROBE, "Found a valid video chain (%s)\n", 18410c0d06caSMauro Carvalho Chehab uvc_print_chain(chain)); 18420c0d06caSMauro Carvalho Chehab 18430c0d06caSMauro Carvalho Chehab list_add_tail(&chain->list, &dev->chains); 18440c0d06caSMauro Carvalho Chehab } 18450c0d06caSMauro Carvalho Chehab 1846e950267aSHenrik Ingo if (list_empty(&dev->chains)) 1847e950267aSHenrik Ingo uvc_scan_fallback(dev); 1848e950267aSHenrik Ingo 18490c0d06caSMauro Carvalho Chehab if (list_empty(&dev->chains)) { 185069df0954SRicardo Ribalda dev_info(&dev->udev->dev, "No valid video chain found.\n"); 18510c0d06caSMauro Carvalho Chehab return -1; 18520c0d06caSMauro Carvalho Chehab } 18530c0d06caSMauro Carvalho Chehab 18542886477fSRicardo Ribalda /* Add GPIO entity to the first chain. */ 18552886477fSRicardo Ribalda if (dev->gpio_unit) { 18562886477fSRicardo Ribalda chain = list_first_entry(&dev->chains, 18572886477fSRicardo Ribalda struct uvc_video_chain, list); 18582886477fSRicardo Ribalda list_add_tail(&dev->gpio_unit->chain, &chain->entities); 18592886477fSRicardo Ribalda } 18602886477fSRicardo Ribalda 18610c0d06caSMauro Carvalho Chehab return 0; 18620c0d06caSMauro Carvalho Chehab } 18630c0d06caSMauro Carvalho Chehab 18640c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 18650c0d06caSMauro Carvalho Chehab * Video device registration and unregistration 18660c0d06caSMauro Carvalho Chehab */ 18670c0d06caSMauro Carvalho Chehab 18680c0d06caSMauro Carvalho Chehab /* 18690c0d06caSMauro Carvalho Chehab * Delete the UVC device. 18700c0d06caSMauro Carvalho Chehab * 18710c0d06caSMauro Carvalho Chehab * Called by the kernel when the last reference to the uvc_device structure 18720c0d06caSMauro Carvalho Chehab * is released. 18730c0d06caSMauro Carvalho Chehab * 18740c0d06caSMauro Carvalho Chehab * As this function is called after or during disconnect(), all URBs have 1875ece41454SKieran Bingham * already been cancelled by the USB core. There is no need to kill the 18760c0d06caSMauro Carvalho Chehab * interrupt URB manually. 18770c0d06caSMauro Carvalho Chehab */ 18789d15cd95SGuennadi Liakhovetski static void uvc_delete(struct kref *kref) 18790c0d06caSMauro Carvalho Chehab { 18809d15cd95SGuennadi Liakhovetski struct uvc_device *dev = container_of(kref, struct uvc_device, ref); 18810c0d06caSMauro Carvalho Chehab struct list_head *p, *n; 18820c0d06caSMauro Carvalho Chehab 18830c0d06caSMauro Carvalho Chehab uvc_status_cleanup(dev); 18840c0d06caSMauro Carvalho Chehab uvc_ctrl_cleanup_device(dev); 18850c0d06caSMauro Carvalho Chehab 18862228d80dSTakashi Iwai usb_put_intf(dev->intf); 18872228d80dSTakashi Iwai usb_put_dev(dev->udev); 18882228d80dSTakashi Iwai 18890c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 18909832e155SJavier Martinez Canillas media_device_cleanup(&dev->mdev); 18910c0d06caSMauro Carvalho Chehab #endif 18920c0d06caSMauro Carvalho Chehab 18930c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->chains) { 18940c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 18952a8c1952SPedro Guilherme Siqueira Moreira 18960c0d06caSMauro Carvalho Chehab chain = list_entry(p, struct uvc_video_chain, list); 18970c0d06caSMauro Carvalho Chehab kfree(chain); 18980c0d06caSMauro Carvalho Chehab } 18990c0d06caSMauro Carvalho Chehab 19000c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->entities) { 19010c0d06caSMauro Carvalho Chehab struct uvc_entity *entity; 19022a8c1952SPedro Guilherme Siqueira Moreira 19030c0d06caSMauro Carvalho Chehab entity = list_entry(p, struct uvc_entity, list); 19040c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 19050c0d06caSMauro Carvalho Chehab uvc_mc_cleanup_entity(entity); 19060c0d06caSMauro Carvalho Chehab #endif 19070c0d06caSMauro Carvalho Chehab kfree(entity); 19080c0d06caSMauro Carvalho Chehab } 19090c0d06caSMauro Carvalho Chehab 19100c0d06caSMauro Carvalho Chehab list_for_each_safe(p, n, &dev->streams) { 19110c0d06caSMauro Carvalho Chehab struct uvc_streaming *streaming; 19122a8c1952SPedro Guilherme Siqueira Moreira 19130c0d06caSMauro Carvalho Chehab streaming = list_entry(p, struct uvc_streaming, list); 19140c0d06caSMauro Carvalho Chehab usb_driver_release_interface(&uvc_driver.driver, 19150c0d06caSMauro Carvalho Chehab streaming->intf); 1916ece41454SKieran Bingham uvc_stream_delete(streaming); 19170c0d06caSMauro Carvalho Chehab } 19180c0d06caSMauro Carvalho Chehab 19190c0d06caSMauro Carvalho Chehab kfree(dev); 19200c0d06caSMauro Carvalho Chehab } 19210c0d06caSMauro Carvalho Chehab 19220c0d06caSMauro Carvalho Chehab static void uvc_release(struct video_device *vdev) 19230c0d06caSMauro Carvalho Chehab { 19240c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream = video_get_drvdata(vdev); 19250c0d06caSMauro Carvalho Chehab struct uvc_device *dev = stream->dev; 19260c0d06caSMauro Carvalho Chehab 19279d15cd95SGuennadi Liakhovetski kref_put(&dev->ref, uvc_delete); 19280c0d06caSMauro Carvalho Chehab } 19290c0d06caSMauro Carvalho Chehab 19300c0d06caSMauro Carvalho Chehab /* 19310c0d06caSMauro Carvalho Chehab * Unregister the video devices. 19320c0d06caSMauro Carvalho Chehab */ 19330c0d06caSMauro Carvalho Chehab static void uvc_unregister_video(struct uvc_device *dev) 19340c0d06caSMauro Carvalho Chehab { 19350c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 19360c0d06caSMauro Carvalho Chehab 19370c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 1938f83f6a6eSRicardo Ribalda /* Nothing to do here, continue. */ 1939d8da7513SHans Verkuil if (!video_is_registered(&stream->vdev)) 19400c0d06caSMauro Carvalho Chehab continue; 19410c0d06caSMauro Carvalho Chehab 1942f83f6a6eSRicardo Ribalda /* 1943f83f6a6eSRicardo Ribalda * For stream->vdev we follow the same logic as: 1944f83f6a6eSRicardo Ribalda * vb2_video_unregister_device(). 1945f83f6a6eSRicardo Ribalda */ 1946f83f6a6eSRicardo Ribalda 1947f83f6a6eSRicardo Ribalda /* 1. Take a reference to vdev */ 1948f83f6a6eSRicardo Ribalda get_device(&stream->vdev.dev); 1949f83f6a6eSRicardo Ribalda 1950f83f6a6eSRicardo Ribalda /* 2. Ensure that no new ioctls can be called. */ 1951d8da7513SHans Verkuil video_unregister_device(&stream->vdev); 1952f83f6a6eSRicardo Ribalda 1953f83f6a6eSRicardo Ribalda /* 3. Wait for old ioctls to finish. */ 1954f83f6a6eSRicardo Ribalda mutex_lock(&stream->mutex); 1955f83f6a6eSRicardo Ribalda 1956f83f6a6eSRicardo Ribalda /* 4. Stop streaming. */ 1957f83f6a6eSRicardo Ribalda uvc_queue_release(&stream->queue); 1958f83f6a6eSRicardo Ribalda 1959f83f6a6eSRicardo Ribalda mutex_unlock(&stream->mutex); 1960f83f6a6eSRicardo Ribalda 1961f83f6a6eSRicardo Ribalda put_device(&stream->vdev.dev); 1962f83f6a6eSRicardo Ribalda 1963f83f6a6eSRicardo Ribalda /* 1964f83f6a6eSRicardo Ribalda * For stream->meta.vdev we can directly call: 1965f83f6a6eSRicardo Ribalda * vb2_video_unregister_device(). 1966f83f6a6eSRicardo Ribalda */ 1967f83f6a6eSRicardo Ribalda vb2_video_unregister_device(&stream->meta.vdev); 1968f83f6a6eSRicardo Ribalda 1969f83f6a6eSRicardo Ribalda /* 1970f83f6a6eSRicardo Ribalda * Now both vdevs are not streaming and all the ioctls will 1971f83f6a6eSRicardo Ribalda * return -ENODEV. 1972f83f6a6eSRicardo Ribalda */ 19730c0d06caSMauro Carvalho Chehab 19740c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup_stream(stream); 19750c0d06caSMauro Carvalho Chehab } 197610e1fdb9SDaniel Axtens 197710e1fdb9SDaniel Axtens uvc_status_unregister(dev); 197810e1fdb9SDaniel Axtens 197910e1fdb9SDaniel Axtens if (dev->vdev.dev) 198010e1fdb9SDaniel Axtens v4l2_device_unregister(&dev->vdev); 198110e1fdb9SDaniel Axtens #ifdef CONFIG_MEDIA_CONTROLLER 198210e1fdb9SDaniel Axtens if (media_devnode_is_registered(dev->mdev.devnode)) 198310e1fdb9SDaniel Axtens media_device_unregister(&dev->mdev); 198410e1fdb9SDaniel Axtens #endif 19850c0d06caSMauro Carvalho Chehab } 19860c0d06caSMauro Carvalho Chehab 198731a96f4cSLaurent Pinchart int uvc_register_video_device(struct uvc_device *dev, 198831a96f4cSLaurent Pinchart struct uvc_streaming *stream, 198931a96f4cSLaurent Pinchart struct video_device *vdev, 199031a96f4cSLaurent Pinchart struct uvc_video_queue *queue, 199131a96f4cSLaurent Pinchart enum v4l2_buf_type type, 199231a96f4cSLaurent Pinchart const struct v4l2_file_operations *fops, 199331a96f4cSLaurent Pinchart const struct v4l2_ioctl_ops *ioctl_ops) 19940c0d06caSMauro Carvalho Chehab { 19950c0d06caSMauro Carvalho Chehab int ret; 19960c0d06caSMauro Carvalho Chehab 1997b83bba24SLaurent Pinchart /* Initialize the video buffers queue. */ 199831a96f4cSLaurent Pinchart ret = uvc_queue_init(queue, type, !uvc_no_drop_param); 1999b83bba24SLaurent Pinchart if (ret) 2000b83bba24SLaurent Pinchart return ret; 2001b83bba24SLaurent Pinchart 20020c0d06caSMauro Carvalho Chehab /* Register the device with V4L. */ 20030c0d06caSMauro Carvalho Chehab 200431a96f4cSLaurent Pinchart /* 200531a96f4cSLaurent Pinchart * We already hold a reference to dev->udev. The video device will be 20060c0d06caSMauro Carvalho Chehab * unregistered before the reference is released, so we don't need to 20070c0d06caSMauro Carvalho Chehab * get another one. 20080c0d06caSMauro Carvalho Chehab */ 20090c0d06caSMauro Carvalho Chehab vdev->v4l2_dev = &dev->vdev; 201031a96f4cSLaurent Pinchart vdev->fops = fops; 201131a96f4cSLaurent Pinchart vdev->ioctl_ops = ioctl_ops; 20120c0d06caSMauro Carvalho Chehab vdev->release = uvc_release; 20130550513cSLaurent Pinchart vdev->prio = &stream->chain->prio; 201431a96f4cSLaurent Pinchart if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 2015954f340fSHans Verkuil vdev->vfl_dir = VFL_DIR_TX; 201631a96f4cSLaurent Pinchart else 201731a96f4cSLaurent Pinchart vdev->vfl_dir = VFL_DIR_RX; 201894c53e26SLaurent Pinchart 201994c53e26SLaurent Pinchart switch (type) { 202094c53e26SLaurent Pinchart case V4L2_BUF_TYPE_VIDEO_CAPTURE: 202194c53e26SLaurent Pinchart default: 202294c53e26SLaurent Pinchart vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; 202394c53e26SLaurent Pinchart break; 202494c53e26SLaurent Pinchart case V4L2_BUF_TYPE_VIDEO_OUTPUT: 202594c53e26SLaurent Pinchart vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; 202694c53e26SLaurent Pinchart break; 2027088ead25SGuennadi Liakhovetski case V4L2_BUF_TYPE_META_CAPTURE: 2028088ead25SGuennadi Liakhovetski vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING; 2029088ead25SGuennadi Liakhovetski break; 203094c53e26SLaurent Pinchart } 203194c53e26SLaurent Pinchart 2032f66dcb32SRicardo Ribalda strscpy(vdev->name, dev->name, sizeof(vdev->name)); 20330c0d06caSMauro Carvalho Chehab 203431a96f4cSLaurent Pinchart /* 203531a96f4cSLaurent Pinchart * Set the driver data before calling video_register_device, otherwise 203631a96f4cSLaurent Pinchart * the file open() handler might race us. 20370c0d06caSMauro Carvalho Chehab */ 20380c0d06caSMauro Carvalho Chehab video_set_drvdata(vdev, stream); 20390c0d06caSMauro Carvalho Chehab 20407fbbbc78SHans Verkuil ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 20410c0d06caSMauro Carvalho Chehab if (ret < 0) { 204269df0954SRicardo Ribalda dev_err(&stream->intf->dev, 204369df0954SRicardo Ribalda "Failed to register %s device (%d).\n", 204431a96f4cSLaurent Pinchart v4l2_type_names[type], ret); 204531a96f4cSLaurent Pinchart return ret; 204631a96f4cSLaurent Pinchart } 204731a96f4cSLaurent Pinchart 204831a96f4cSLaurent Pinchart kref_get(&dev->ref); 204931a96f4cSLaurent Pinchart return 0; 205031a96f4cSLaurent Pinchart } 205131a96f4cSLaurent Pinchart 205231a96f4cSLaurent Pinchart static int uvc_register_video(struct uvc_device *dev, 205331a96f4cSLaurent Pinchart struct uvc_streaming *stream) 205431a96f4cSLaurent Pinchart { 205531a96f4cSLaurent Pinchart int ret; 205631a96f4cSLaurent Pinchart 205731a96f4cSLaurent Pinchart /* Initialize the streaming interface with default parameters. */ 205831a96f4cSLaurent Pinchart ret = uvc_video_init(stream); 205931a96f4cSLaurent Pinchart if (ret < 0) { 206069df0954SRicardo Ribalda dev_err(&stream->intf->dev, 206169df0954SRicardo Ribalda "Failed to initialize the device (%d).\n", ret); 20620c0d06caSMauro Carvalho Chehab return ret; 20630c0d06caSMauro Carvalho Chehab } 20640c0d06caSMauro Carvalho Chehab 2065f887e99aSLaurent Pinchart if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 2066088ead25SGuennadi Liakhovetski stream->chain->caps |= V4L2_CAP_VIDEO_CAPTURE 2067088ead25SGuennadi Liakhovetski | V4L2_CAP_META_CAPTURE; 2068f887e99aSLaurent Pinchart else 2069f887e99aSLaurent Pinchart stream->chain->caps |= V4L2_CAP_VIDEO_OUTPUT; 2070f887e99aSLaurent Pinchart 207131a96f4cSLaurent Pinchart uvc_debugfs_init_stream(stream); 207231a96f4cSLaurent Pinchart 207331a96f4cSLaurent Pinchart /* Register the device with V4L. */ 207431a96f4cSLaurent Pinchart return uvc_register_video_device(dev, stream, &stream->vdev, 207531a96f4cSLaurent Pinchart &stream->queue, stream->type, 207631a96f4cSLaurent Pinchart &uvc_fops, &uvc_ioctl_ops); 20770c0d06caSMauro Carvalho Chehab } 20780c0d06caSMauro Carvalho Chehab 20790c0d06caSMauro Carvalho Chehab /* 20800c0d06caSMauro Carvalho Chehab * Register all video devices in all chains. 20810c0d06caSMauro Carvalho Chehab */ 20820c0d06caSMauro Carvalho Chehab static int uvc_register_terms(struct uvc_device *dev, 20830c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain) 20840c0d06caSMauro Carvalho Chehab { 20850c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 20860c0d06caSMauro Carvalho Chehab struct uvc_entity *term; 20870c0d06caSMauro Carvalho Chehab int ret; 20880c0d06caSMauro Carvalho Chehab 20890c0d06caSMauro Carvalho Chehab list_for_each_entry(term, &chain->entities, chain) { 20900c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) 20910c0d06caSMauro Carvalho Chehab continue; 20920c0d06caSMauro Carvalho Chehab 20930c0d06caSMauro Carvalho Chehab stream = uvc_stream_by_id(dev, term->id); 20940c0d06caSMauro Carvalho Chehab if (stream == NULL) { 209569df0954SRicardo Ribalda dev_info(&dev->udev->dev, 209669df0954SRicardo Ribalda "No streaming interface found for terminal %u.", 209769df0954SRicardo Ribalda term->id); 20980c0d06caSMauro Carvalho Chehab continue; 20990c0d06caSMauro Carvalho Chehab } 21000c0d06caSMauro Carvalho Chehab 21010c0d06caSMauro Carvalho Chehab stream->chain = chain; 21020c0d06caSMauro Carvalho Chehab ret = uvc_register_video(dev, stream); 21030c0d06caSMauro Carvalho Chehab if (ret < 0) 21040c0d06caSMauro Carvalho Chehab return ret; 21050c0d06caSMauro Carvalho Chehab 2106699b9a86SLaurent Pinchart /* 2107699b9a86SLaurent Pinchart * Register a metadata node, but ignore a possible failure, 2108088ead25SGuennadi Liakhovetski * complete registration of video nodes anyway. 2109088ead25SGuennadi Liakhovetski */ 2110088ead25SGuennadi Liakhovetski uvc_meta_register(stream); 2111088ead25SGuennadi Liakhovetski 2112d8da7513SHans Verkuil term->vdev = &stream->vdev; 21130c0d06caSMauro Carvalho Chehab } 21140c0d06caSMauro Carvalho Chehab 21150c0d06caSMauro Carvalho Chehab return 0; 21160c0d06caSMauro Carvalho Chehab } 21170c0d06caSMauro Carvalho Chehab 21180c0d06caSMauro Carvalho Chehab static int uvc_register_chains(struct uvc_device *dev) 21190c0d06caSMauro Carvalho Chehab { 21200c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain; 21210c0d06caSMauro Carvalho Chehab int ret; 21220c0d06caSMauro Carvalho Chehab 21230c0d06caSMauro Carvalho Chehab list_for_each_entry(chain, &dev->chains, list) { 21240c0d06caSMauro Carvalho Chehab ret = uvc_register_terms(dev, chain); 21250c0d06caSMauro Carvalho Chehab if (ret < 0) 21260c0d06caSMauro Carvalho Chehab return ret; 21270c0d06caSMauro Carvalho Chehab 21280c0d06caSMauro Carvalho Chehab #ifdef CONFIG_MEDIA_CONTROLLER 21290c0d06caSMauro Carvalho Chehab ret = uvc_mc_register_entities(chain); 21306689df06SColin Ian King if (ret < 0) 213169df0954SRicardo Ribalda dev_info(&dev->udev->dev, 21326689df06SColin Ian King "Failed to register entities (%d).\n", ret); 21330c0d06caSMauro Carvalho Chehab #endif 21340c0d06caSMauro Carvalho Chehab } 21350c0d06caSMauro Carvalho Chehab 21360c0d06caSMauro Carvalho Chehab return 0; 21370c0d06caSMauro Carvalho Chehab } 21380c0d06caSMauro Carvalho Chehab 21390c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 21400c0d06caSMauro Carvalho Chehab * USB probe, disconnect, suspend and resume 21410c0d06caSMauro Carvalho Chehab */ 21420c0d06caSMauro Carvalho Chehab 21433a03284dSLaurent Pinchart static const struct uvc_device_info uvc_quirk_none = { 0 }; 21443bc85817SGuennadi Liakhovetski 21450c0d06caSMauro Carvalho Chehab static int uvc_probe(struct usb_interface *intf, 21460c0d06caSMauro Carvalho Chehab const struct usb_device_id *id) 21470c0d06caSMauro Carvalho Chehab { 21480c0d06caSMauro Carvalho Chehab struct usb_device *udev = interface_to_usbdev(intf); 21490c0d06caSMauro Carvalho Chehab struct uvc_device *dev; 21503bc85817SGuennadi Liakhovetski const struct uvc_device_info *info = 21513bc85817SGuennadi Liakhovetski (const struct uvc_device_info *)id->driver_info; 2152e7b09f18SPeter Boström int function; 21530c0d06caSMauro Carvalho Chehab int ret; 21540c0d06caSMauro Carvalho Chehab 21550c0d06caSMauro Carvalho Chehab /* Allocate memory for the device and initialize it. */ 2156f14d4988SLaurent Pinchart dev = kzalloc(sizeof(*dev), GFP_KERNEL); 2157f14d4988SLaurent Pinchart if (dev == NULL) 21580c0d06caSMauro Carvalho Chehab return -ENOMEM; 21590c0d06caSMauro Carvalho Chehab 21600c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->entities); 21610c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->chains); 21620c0d06caSMauro Carvalho Chehab INIT_LIST_HEAD(&dev->streams); 21639d15cd95SGuennadi Liakhovetski kref_init(&dev->ref); 21640c0d06caSMauro Carvalho Chehab atomic_set(&dev->nmappings, 0); 216517706f56SLaurent Pinchart mutex_init(&dev->lock); 21660c0d06caSMauro Carvalho Chehab 21670c0d06caSMauro Carvalho Chehab dev->udev = usb_get_dev(udev); 21680c0d06caSMauro Carvalho Chehab dev->intf = usb_get_intf(intf); 21690c0d06caSMauro Carvalho Chehab dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; 21703a03284dSLaurent Pinchart dev->info = info ? info : &uvc_quirk_none; 21713a03284dSLaurent Pinchart dev->quirks = uvc_quirks_param == -1 21723a03284dSLaurent Pinchart ? dev->info->quirks : uvc_quirks_param; 21730c0d06caSMauro Carvalho Chehab 2174ed4c5fa4SRicardo Ribalda if (id->idVendor && id->idProduct) 21759e56380aSJoe Perches uvc_dbg(dev, PROBE, "Probing known UVC device %s (%04x:%04x)\n", 2176ed4c5fa4SRicardo Ribalda udev->devpath, id->idVendor, id->idProduct); 2177ed4c5fa4SRicardo Ribalda else 21789e56380aSJoe Perches uvc_dbg(dev, PROBE, "Probing generic UVC device %s\n", 21799e56380aSJoe Perches udev->devpath); 2180ed4c5fa4SRicardo Ribalda 21810c0d06caSMauro Carvalho Chehab if (udev->product != NULL) 2182c0decac1SMauro Carvalho Chehab strscpy(dev->name, udev->product, sizeof(dev->name)); 21830c0d06caSMauro Carvalho Chehab else 2184f14d4988SLaurent Pinchart snprintf(dev->name, sizeof(dev->name), 21850c0d06caSMauro Carvalho Chehab "UVC Camera (%04x:%04x)", 21860c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idVendor), 21870c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idProduct)); 21880c0d06caSMauro Carvalho Chehab 2189e7b09f18SPeter Boström /* 2190e7b09f18SPeter Boström * Add iFunction or iInterface to names when available as additional 2191e7b09f18SPeter Boström * distinguishers between interfaces. iFunction is prioritized over 2192e7b09f18SPeter Boström * iInterface which matches Windows behavior at the point of writing. 2193e7b09f18SPeter Boström */ 2194e7b09f18SPeter Boström if (intf->intf_assoc && intf->intf_assoc->iFunction != 0) 2195e7b09f18SPeter Boström function = intf->intf_assoc->iFunction; 2196e7b09f18SPeter Boström else 2197e7b09f18SPeter Boström function = intf->cur_altsetting->desc.iInterface; 2198e7b09f18SPeter Boström if (function != 0) { 2199e7b09f18SPeter Boström size_t len; 2200e7b09f18SPeter Boström 2201e7b09f18SPeter Boström strlcat(dev->name, ": ", sizeof(dev->name)); 2202e7b09f18SPeter Boström len = strlen(dev->name); 2203e7b09f18SPeter Boström usb_string(udev, function, dev->name + len, 2204e7b09f18SPeter Boström sizeof(dev->name) - len); 2205e7b09f18SPeter Boström } 2206e7b09f18SPeter Boström 22078c279e93SLaurent Pinchart /* Initialize the media device. */ 22088c279e93SLaurent Pinchart #ifdef CONFIG_MEDIA_CONTROLLER 22098c279e93SLaurent Pinchart dev->mdev.dev = &intf->dev; 22108c279e93SLaurent Pinchart strscpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model)); 22118c279e93SLaurent Pinchart if (udev->serial) 22128c279e93SLaurent Pinchart strscpy(dev->mdev.serial, udev->serial, 22138c279e93SLaurent Pinchart sizeof(dev->mdev.serial)); 22148c279e93SLaurent Pinchart usb_make_path(udev, dev->mdev.bus_info, sizeof(dev->mdev.bus_info)); 22158c279e93SLaurent Pinchart dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); 22168c279e93SLaurent Pinchart media_device_init(&dev->mdev); 22178c279e93SLaurent Pinchart 22188c279e93SLaurent Pinchart dev->vdev.mdev = &dev->mdev; 22198c279e93SLaurent Pinchart #endif 22208c279e93SLaurent Pinchart 22210c0d06caSMauro Carvalho Chehab /* Parse the Video Class control descriptor. */ 22220c0d06caSMauro Carvalho Chehab if (uvc_parse_control(dev) < 0) { 22239e56380aSJoe Perches uvc_dbg(dev, PROBE, "Unable to parse UVC descriptors\n"); 22240c0d06caSMauro Carvalho Chehab goto error; 22250c0d06caSMauro Carvalho Chehab } 22260c0d06caSMauro Carvalho Chehab 22272886477fSRicardo Ribalda /* Parse the associated GPIOs. */ 22282886477fSRicardo Ribalda if (uvc_gpio_parse(dev) < 0) { 22299e56380aSJoe Perches uvc_dbg(dev, PROBE, "Unable to parse UVC GPIOs\n"); 22302886477fSRicardo Ribalda goto error; 22312886477fSRicardo Ribalda } 22322886477fSRicardo Ribalda 223369df0954SRicardo Ribalda dev_info(&dev->udev->dev, "Found UVC %u.%02x device %s (%04x:%04x)\n", 22340c0d06caSMauro Carvalho Chehab dev->uvc_version >> 8, dev->uvc_version & 0xff, 22350c0d06caSMauro Carvalho Chehab udev->product ? udev->product : "<unnamed>", 22360c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idVendor), 22370c0d06caSMauro Carvalho Chehab le16_to_cpu(udev->descriptor.idProduct)); 22380c0d06caSMauro Carvalho Chehab 22393a03284dSLaurent Pinchart if (dev->quirks != dev->info->quirks) { 224069df0954SRicardo Ribalda dev_info(&dev->udev->dev, 224169df0954SRicardo Ribalda "Forcing device quirks to 0x%x by module parameter for testing purpose.\n", 224269df0954SRicardo Ribalda dev->quirks); 224369df0954SRicardo Ribalda dev_info(&dev->udev->dev, 2244e3a0f556SJonathan Neuschäfer "Please report required quirks to the linux-media mailing list.\n"); 22450c0d06caSMauro Carvalho Chehab } 22460c0d06caSMauro Carvalho Chehab 2247b400b6f2SLaurent Pinchart if (dev->info->uvc_version) { 2248b400b6f2SLaurent Pinchart dev->uvc_version = dev->info->uvc_version; 224969df0954SRicardo Ribalda dev_info(&dev->udev->dev, "Forcing UVC version to %u.%02x\n", 2250b400b6f2SLaurent Pinchart dev->uvc_version >> 8, dev->uvc_version & 0xff); 2251b400b6f2SLaurent Pinchart } 2252b400b6f2SLaurent Pinchart 22538c279e93SLaurent Pinchart /* Register the V4L2 device. */ 22540c0d06caSMauro Carvalho Chehab if (v4l2_device_register(&intf->dev, &dev->vdev) < 0) 22550c0d06caSMauro Carvalho Chehab goto error; 22560c0d06caSMauro Carvalho Chehab 22570c0d06caSMauro Carvalho Chehab /* Scan the device for video chains. */ 22580c0d06caSMauro Carvalho Chehab if (uvc_scan_device(dev) < 0) 22590c0d06caSMauro Carvalho Chehab goto error; 22600c0d06caSMauro Carvalho Chehab 2261866c6bddSRicardo Ribalda /* Initialize controls. */ 2262866c6bddSRicardo Ribalda if (uvc_ctrl_init_device(dev) < 0) 2263866c6bddSRicardo Ribalda goto error; 2264866c6bddSRicardo Ribalda 22650c0d06caSMauro Carvalho Chehab /* Register video device nodes. */ 22660c0d06caSMauro Carvalho Chehab if (uvc_register_chains(dev) < 0) 22670c0d06caSMauro Carvalho Chehab goto error; 22680c0d06caSMauro Carvalho Chehab 22699832e155SJavier Martinez Canillas #ifdef CONFIG_MEDIA_CONTROLLER 22709832e155SJavier Martinez Canillas /* Register the media device node */ 22719832e155SJavier Martinez Canillas if (media_device_register(&dev->mdev) < 0) 22729832e155SJavier Martinez Canillas goto error; 22739832e155SJavier Martinez Canillas #endif 22740c0d06caSMauro Carvalho Chehab /* Save our data pointer in the interface data. */ 22750c0d06caSMauro Carvalho Chehab usb_set_intfdata(intf, dev); 22760c0d06caSMauro Carvalho Chehab 22770c0d06caSMauro Carvalho Chehab /* Initialize the interrupt URB. */ 22787b78a846SPedro Guilherme Siqueira Moreira ret = uvc_status_init(dev); 22797b78a846SPedro Guilherme Siqueira Moreira if (ret < 0) { 228069df0954SRicardo Ribalda dev_info(&dev->udev->dev, 228169df0954SRicardo Ribalda "Unable to initialize the status endpoint (%d), status interrupt will not be supported.\n", 228269df0954SRicardo Ribalda ret); 22830c0d06caSMauro Carvalho Chehab } 22840c0d06caSMauro Carvalho Chehab 22852886477fSRicardo Ribalda ret = uvc_gpio_init_irq(dev); 22862886477fSRicardo Ribalda if (ret < 0) { 22872886477fSRicardo Ribalda dev_err(&dev->udev->dev, 22882886477fSRicardo Ribalda "Unable to request privacy GPIO IRQ (%d)\n", ret); 22892886477fSRicardo Ribalda goto error; 22902886477fSRicardo Ribalda } 22912886477fSRicardo Ribalda 22924b3421c2SRicardo Ribalda if (dev->quirks & UVC_QUIRK_NO_RESET_RESUME) 22934b3421c2SRicardo Ribalda udev->quirks &= ~USB_QUIRK_RESET_RESUME; 22944b3421c2SRicardo Ribalda 2295991df394SRicardo Ribalda if (!(dev->quirks & UVC_QUIRK_DISABLE_AUTOSUSPEND)) 22960c0d06caSMauro Carvalho Chehab usb_enable_autosuspend(udev); 2297991df394SRicardo Ribalda 2298991df394SRicardo Ribalda uvc_dbg(dev, PROBE, "UVC device initialized\n"); 2299991df394SRicardo Ribalda 23000c0d06caSMauro Carvalho Chehab return 0; 23010c0d06caSMauro Carvalho Chehab 23020c0d06caSMauro Carvalho Chehab error: 23030c0d06caSMauro Carvalho Chehab uvc_unregister_video(dev); 2304f9ffcb0aSPhilipp Zabel kref_put(&dev->ref, uvc_delete); 23050c0d06caSMauro Carvalho Chehab return -ENODEV; 23060c0d06caSMauro Carvalho Chehab } 23070c0d06caSMauro Carvalho Chehab 23080c0d06caSMauro Carvalho Chehab static void uvc_disconnect(struct usb_interface *intf) 23090c0d06caSMauro Carvalho Chehab { 23100c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 23110c0d06caSMauro Carvalho Chehab 2312699b9a86SLaurent Pinchart /* 2313699b9a86SLaurent Pinchart * Set the USB interface data to NULL. This can be done outside the 23140c0d06caSMauro Carvalho Chehab * lock, as there's no other reader. 23150c0d06caSMauro Carvalho Chehab */ 23160c0d06caSMauro Carvalho Chehab usb_set_intfdata(intf, NULL); 23170c0d06caSMauro Carvalho Chehab 23180c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 23190c0d06caSMauro Carvalho Chehab UVC_SC_VIDEOSTREAMING) 23200c0d06caSMauro Carvalho Chehab return; 23210c0d06caSMauro Carvalho Chehab 23220c0d06caSMauro Carvalho Chehab uvc_unregister_video(dev); 2323f9ffcb0aSPhilipp Zabel kref_put(&dev->ref, uvc_delete); 23240c0d06caSMauro Carvalho Chehab } 23250c0d06caSMauro Carvalho Chehab 23260c0d06caSMauro Carvalho Chehab static int uvc_suspend(struct usb_interface *intf, pm_message_t message) 23270c0d06caSMauro Carvalho Chehab { 23280c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 23290c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 23300c0d06caSMauro Carvalho Chehab 23319e56380aSJoe Perches uvc_dbg(dev, SUSPEND, "Suspending interface %u\n", 23320c0d06caSMauro Carvalho Chehab intf->cur_altsetting->desc.bInterfaceNumber); 23330c0d06caSMauro Carvalho Chehab 23340c0d06caSMauro Carvalho Chehab /* Controls are cached on the fly so they don't need to be saved. */ 23350c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 233617706f56SLaurent Pinchart UVC_SC_VIDEOCONTROL) { 233717706f56SLaurent Pinchart mutex_lock(&dev->lock); 233817706f56SLaurent Pinchart if (dev->users) 233917706f56SLaurent Pinchart uvc_status_stop(dev); 234017706f56SLaurent Pinchart mutex_unlock(&dev->lock); 234117706f56SLaurent Pinchart return 0; 234217706f56SLaurent Pinchart } 23430c0d06caSMauro Carvalho Chehab 23440c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 23450c0d06caSMauro Carvalho Chehab if (stream->intf == intf) 23460c0d06caSMauro Carvalho Chehab return uvc_video_suspend(stream); 23470c0d06caSMauro Carvalho Chehab } 23480c0d06caSMauro Carvalho Chehab 23499e56380aSJoe Perches uvc_dbg(dev, SUSPEND, 23509e56380aSJoe Perches "Suspend: video streaming USB interface mismatch\n"); 23510c0d06caSMauro Carvalho Chehab return -EINVAL; 23520c0d06caSMauro Carvalho Chehab } 23530c0d06caSMauro Carvalho Chehab 23540c0d06caSMauro Carvalho Chehab static int __uvc_resume(struct usb_interface *intf, int reset) 23550c0d06caSMauro Carvalho Chehab { 23560c0d06caSMauro Carvalho Chehab struct uvc_device *dev = usb_get_intfdata(intf); 23570c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream; 2358b83bba24SLaurent Pinchart int ret = 0; 23590c0d06caSMauro Carvalho Chehab 23609e56380aSJoe Perches uvc_dbg(dev, SUSPEND, "Resuming interface %u\n", 23610c0d06caSMauro Carvalho Chehab intf->cur_altsetting->desc.bInterfaceNumber); 23620c0d06caSMauro Carvalho Chehab 23630c0d06caSMauro Carvalho Chehab if (intf->cur_altsetting->desc.bInterfaceSubClass == 23640c0d06caSMauro Carvalho Chehab UVC_SC_VIDEOCONTROL) { 236517706f56SLaurent Pinchart if (reset) { 236617e1319fSWilliam Manley ret = uvc_ctrl_restore_values(dev); 23670c0d06caSMauro Carvalho Chehab if (ret < 0) 23680c0d06caSMauro Carvalho Chehab return ret; 23690c0d06caSMauro Carvalho Chehab } 23700c0d06caSMauro Carvalho Chehab 237117706f56SLaurent Pinchart mutex_lock(&dev->lock); 237217706f56SLaurent Pinchart if (dev->users) 237317706f56SLaurent Pinchart ret = uvc_status_start(dev, GFP_NOIO); 237417706f56SLaurent Pinchart mutex_unlock(&dev->lock); 237517706f56SLaurent Pinchart 237617706f56SLaurent Pinchart return ret; 23770c0d06caSMauro Carvalho Chehab } 23780c0d06caSMauro Carvalho Chehab 23790c0d06caSMauro Carvalho Chehab list_for_each_entry(stream, &dev->streams, list) { 2380b83bba24SLaurent Pinchart if (stream->intf == intf) { 2381b83bba24SLaurent Pinchart ret = uvc_video_resume(stream, reset); 2382b83bba24SLaurent Pinchart if (ret < 0) 23830da4ab98SLaurent Pinchart uvc_queue_streamoff(&stream->queue, 23840da4ab98SLaurent Pinchart stream->queue.queue.type); 2385b83bba24SLaurent Pinchart return ret; 2386b83bba24SLaurent Pinchart } 23870c0d06caSMauro Carvalho Chehab } 23880c0d06caSMauro Carvalho Chehab 23899e56380aSJoe Perches uvc_dbg(dev, SUSPEND, 23909e56380aSJoe Perches "Resume: video streaming USB interface mismatch\n"); 23910c0d06caSMauro Carvalho Chehab return -EINVAL; 23920c0d06caSMauro Carvalho Chehab } 23930c0d06caSMauro Carvalho Chehab 23940c0d06caSMauro Carvalho Chehab static int uvc_resume(struct usb_interface *intf) 23950c0d06caSMauro Carvalho Chehab { 23960c0d06caSMauro Carvalho Chehab return __uvc_resume(intf, 0); 23970c0d06caSMauro Carvalho Chehab } 23980c0d06caSMauro Carvalho Chehab 23990c0d06caSMauro Carvalho Chehab static int uvc_reset_resume(struct usb_interface *intf) 24000c0d06caSMauro Carvalho Chehab { 24010c0d06caSMauro Carvalho Chehab return __uvc_resume(intf, 1); 24020c0d06caSMauro Carvalho Chehab } 24030c0d06caSMauro Carvalho Chehab 24040c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 24050c0d06caSMauro Carvalho Chehab * Module parameters 24060c0d06caSMauro Carvalho Chehab */ 24070c0d06caSMauro Carvalho Chehab 2408e4dca7b7SKees Cook static int uvc_clock_param_get(char *buffer, const struct kernel_param *kp) 24090c0d06caSMauro Carvalho Chehab { 24100c0d06caSMauro Carvalho Chehab if (uvc_clock_param == CLOCK_MONOTONIC) 24110c0d06caSMauro Carvalho Chehab return sprintf(buffer, "CLOCK_MONOTONIC"); 24120c0d06caSMauro Carvalho Chehab else 24130c0d06caSMauro Carvalho Chehab return sprintf(buffer, "CLOCK_REALTIME"); 24140c0d06caSMauro Carvalho Chehab } 24150c0d06caSMauro Carvalho Chehab 2416e4dca7b7SKees Cook static int uvc_clock_param_set(const char *val, const struct kernel_param *kp) 24170c0d06caSMauro Carvalho Chehab { 24180c0d06caSMauro Carvalho Chehab if (strncasecmp(val, "clock_", strlen("clock_")) == 0) 24190c0d06caSMauro Carvalho Chehab val += strlen("clock_"); 24200c0d06caSMauro Carvalho Chehab 24210c0d06caSMauro Carvalho Chehab if (strcasecmp(val, "monotonic") == 0) 24220c0d06caSMauro Carvalho Chehab uvc_clock_param = CLOCK_MONOTONIC; 24230c0d06caSMauro Carvalho Chehab else if (strcasecmp(val, "realtime") == 0) 24240c0d06caSMauro Carvalho Chehab uvc_clock_param = CLOCK_REALTIME; 24250c0d06caSMauro Carvalho Chehab else 24260c0d06caSMauro Carvalho Chehab return -EINVAL; 24270c0d06caSMauro Carvalho Chehab 24280c0d06caSMauro Carvalho Chehab return 0; 24290c0d06caSMauro Carvalho Chehab } 24300c0d06caSMauro Carvalho Chehab 24310c0d06caSMauro Carvalho Chehab module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get, 24320ce75d5eSPedro Guilherme Siqueira Moreira &uvc_clock_param, 0644); 24330c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(clock, "Video buffers timestamp clock"); 24340ce75d5eSPedro Guilherme Siqueira Moreira module_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, 0644); 24355d0fd3c8SLaurent Pinchart MODULE_PARM_DESC(hwtimestamps, "Use hardware timestamps"); 24360ce75d5eSPedro Guilherme Siqueira Moreira module_param_named(nodrop, uvc_no_drop_param, uint, 0644); 24370c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); 24380ce75d5eSPedro Guilherme Siqueira Moreira module_param_named(quirks, uvc_quirks_param, uint, 0644); 24390c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(quirks, "Forced device quirks"); 24400ce75d5eSPedro Guilherme Siqueira Moreira module_param_named(trace, uvc_dbg_param, uint, 0644); 24410c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(trace, "Trace level bitmask"); 24420ce75d5eSPedro Guilherme Siqueira Moreira module_param_named(timeout, uvc_timeout_param, uint, 0644); 24430c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); 24440c0d06caSMauro Carvalho Chehab 24450c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------ 24460c0d06caSMauro Carvalho Chehab * Driver initialization and cleanup 24470c0d06caSMauro Carvalho Chehab */ 24480c0d06caSMauro Carvalho Chehab 244938207560SRicardo Ribalda static const struct uvc_device_info uvc_ctrl_power_line_limited = { 245038207560SRicardo Ribalda .mappings = (const struct uvc_control_mapping *[]) { 245138207560SRicardo Ribalda &uvc_ctrl_power_line_mapping_limited, 245238207560SRicardo Ribalda NULL, /* Sentinel */ 245338207560SRicardo Ribalda }, 245438207560SRicardo Ribalda }; 245538207560SRicardo Ribalda 2456a7c28150SRicardo Ribalda static const struct uvc_device_info uvc_ctrl_power_line_uvc11 = { 2457a7c28150SRicardo Ribalda .mappings = (const struct uvc_control_mapping *[]) { 2458a7c28150SRicardo Ribalda &uvc_ctrl_power_line_mapping_uvc11, 2459a7c28150SRicardo Ribalda NULL, /* Sentinel */ 2460a7c28150SRicardo Ribalda }, 2461a7c28150SRicardo Ribalda }; 2462a7c28150SRicardo Ribalda 24633bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_probe_minmax = { 24643bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_PROBE_MINMAX, 24653bc85817SGuennadi Liakhovetski }; 24663bc85817SGuennadi Liakhovetski 24673bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_fix_bandwidth = { 24683bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_FIX_BANDWIDTH, 24693bc85817SGuennadi Liakhovetski }; 24703bc85817SGuennadi Liakhovetski 24713bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_probe_def = { 24723bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_PROBE_DEF, 24733bc85817SGuennadi Liakhovetski }; 24743bc85817SGuennadi Liakhovetski 24753bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_stream_no_fid = { 24763bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_STREAM_NO_FID, 24773bc85817SGuennadi Liakhovetski }; 24783bc85817SGuennadi Liakhovetski 24793bc85817SGuennadi Liakhovetski static const struct uvc_device_info uvc_quirk_force_y8 = { 24803bc85817SGuennadi Liakhovetski .quirks = UVC_QUIRK_FORCE_Y8, 24813bc85817SGuennadi Liakhovetski }; 24823bc85817SGuennadi Liakhovetski 248388d8034cSGuennadi Liakhovetski #define UVC_INFO_QUIRK(q) (kernel_ulong_t)&(struct uvc_device_info){.quirks = q} 24846ea0d588SGuennadi Liakhovetski #define UVC_INFO_META(m) (kernel_ulong_t)&(struct uvc_device_info) \ 24856ea0d588SGuennadi Liakhovetski {.meta_format = m} 24863bc85817SGuennadi Liakhovetski 24870c0d06caSMauro Carvalho Chehab /* 24880c0d06caSMauro Carvalho Chehab * The Logitech cameras listed below have their interface class set to 24890c0d06caSMauro Carvalho Chehab * VENDOR_SPEC because they don't announce themselves as UVC devices, even 24900c0d06caSMauro Carvalho Chehab * though they are compliant. 249131f9b4a2SDavid Given * 249231f9b4a2SDavid Given * Sort these by vendor/product ID. 24930c0d06caSMauro Carvalho Chehab */ 24947fb2e072SArvind Yadav static const struct usb_device_id uvc_ids[] = { 249538207560SRicardo Ribalda /* Quanta USB2.0 HD UVC Webcam */ 249638207560SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 249738207560SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 249838207560SRicardo Ribalda .idVendor = 0x0408, 249938207560SRicardo Ribalda .idProduct = 0x3090, 250038207560SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 250138207560SRicardo Ribalda .bInterfaceSubClass = 1, 250238207560SRicardo Ribalda .bInterfaceProtocol = 0, 250338207560SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 250495f03d97SRicardo Ribalda /* Quanta USB2.0 HD UVC Webcam */ 250595f03d97SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 250695f03d97SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 250795f03d97SRicardo Ribalda .idVendor = 0x0408, 250895f03d97SRicardo Ribalda .idProduct = 0x4030, 250995f03d97SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 251095f03d97SRicardo Ribalda .bInterfaceSubClass = 1, 251195f03d97SRicardo Ribalda .bInterfaceProtocol = 0, 251295f03d97SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 251395f03d97SRicardo Ribalda /* Quanta USB2.0 HD UVC Webcam */ 251495f03d97SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 251595f03d97SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 251695f03d97SRicardo Ribalda .idVendor = 0x0408, 251795f03d97SRicardo Ribalda .idProduct = 0x4034, 251895f03d97SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 251995f03d97SRicardo Ribalda .bInterfaceSubClass = 1, 2520150f7b11SRicardo Ribalda .bInterfaceProtocol = UVC_PC_PROTOCOL_15, 252195f03d97SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 2522*9471b8f8SLaurent Pinchart /* Quanta ACER HD User Facing */ 2523*9471b8f8SLaurent Pinchart { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2524*9471b8f8SLaurent Pinchart | USB_DEVICE_ID_MATCH_INT_INFO, 2525*9471b8f8SLaurent Pinchart .idVendor = 0x0408, 2526*9471b8f8SLaurent Pinchart .idProduct = 0x4035, 2527*9471b8f8SLaurent Pinchart .bInterfaceClass = USB_CLASS_VIDEO, 2528*9471b8f8SLaurent Pinchart .bInterfaceSubClass = 1, 2529*9471b8f8SLaurent Pinchart .bInterfaceProtocol = UVC_PC_PROTOCOL_15, 2530*9471b8f8SLaurent Pinchart .driver_info = (kernel_ulong_t)&(const struct uvc_device_info){ 2531*9471b8f8SLaurent Pinchart .uvc_version = 0x010a, 2532*9471b8f8SLaurent Pinchart } }, 25330c0d06caSMauro Carvalho Chehab /* LogiLink Wireless Webcam */ 25340c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25350c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 25360c0d06caSMauro Carvalho Chehab .idVendor = 0x0416, 25370c0d06caSMauro Carvalho Chehab .idProduct = 0xa91a, 25380c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 25390c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 25400c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 25413bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 25420c0d06caSMauro Carvalho Chehab /* Genius eFace 2025 */ 25430c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25440c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 25450c0d06caSMauro Carvalho Chehab .idVendor = 0x0458, 25460c0d06caSMauro Carvalho Chehab .idProduct = 0x706e, 25470c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 25480c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 25490c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 25503bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 25510c0d06caSMauro Carvalho Chehab /* Microsoft Lifecam NX-6000 */ 25520c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25530c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 25540c0d06caSMauro Carvalho Chehab .idVendor = 0x045e, 25550c0d06caSMauro Carvalho Chehab .idProduct = 0x00f8, 25560c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 25570c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 25580c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 25593bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 25601558ec83SLaurent Pinchart /* Microsoft Lifecam NX-3000 */ 25611558ec83SLaurent Pinchart { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25621558ec83SLaurent Pinchart | USB_DEVICE_ID_MATCH_INT_INFO, 25631558ec83SLaurent Pinchart .idVendor = 0x045e, 25641558ec83SLaurent Pinchart .idProduct = 0x0721, 25651558ec83SLaurent Pinchart .bInterfaceClass = USB_CLASS_VIDEO, 25661558ec83SLaurent Pinchart .bInterfaceSubClass = 1, 25671558ec83SLaurent Pinchart .bInterfaceProtocol = 0, 25683bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 25690c0d06caSMauro Carvalho Chehab /* Microsoft Lifecam VX-7000 */ 25700c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25710c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 25720c0d06caSMauro Carvalho Chehab .idVendor = 0x045e, 25730c0d06caSMauro Carvalho Chehab .idProduct = 0x0723, 25740c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 25750c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 25760c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 25773bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 2578136effa7SRicardo Ribalda /* Logitech, Webcam C910 */ 2579136effa7SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2580136effa7SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 2581136effa7SRicardo Ribalda .idVendor = 0x046d, 2582136effa7SRicardo Ribalda .idProduct = 0x0821, 2583136effa7SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 2584136effa7SRicardo Ribalda .bInterfaceSubClass = 1, 2585136effa7SRicardo Ribalda .bInterfaceProtocol = 0, 2586136effa7SRicardo Ribalda .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_WAKE_AUTOSUSPEND)}, 2587136effa7SRicardo Ribalda /* Logitech, Webcam B910 */ 2588136effa7SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2589136effa7SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 2590136effa7SRicardo Ribalda .idVendor = 0x046d, 2591136effa7SRicardo Ribalda .idProduct = 0x0823, 2592136effa7SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 2593136effa7SRicardo Ribalda .bInterfaceSubClass = 1, 2594136effa7SRicardo Ribalda .bInterfaceProtocol = 0, 2595136effa7SRicardo Ribalda .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_WAKE_AUTOSUSPEND)}, 25960c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Fusion */ 25970c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 25980c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 25990c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 26000c0d06caSMauro Carvalho Chehab .idProduct = 0x08c1, 26010c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 26020c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26030c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 26040c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Orbit MP */ 26050c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26060c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26070c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 26080c0d06caSMauro Carvalho Chehab .idProduct = 0x08c2, 26090c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 26100c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26110c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 26120c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Pro for Notebook */ 26130c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26140c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26150c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 26160c0d06caSMauro Carvalho Chehab .idProduct = 0x08c3, 26170c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 26180c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26190c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 26200c0d06caSMauro Carvalho Chehab /* Logitech Quickcam Pro 5000 */ 26210c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26220c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26230c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 26240c0d06caSMauro Carvalho Chehab .idProduct = 0x08c5, 26250c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 26260c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26270c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 26280c0d06caSMauro Carvalho Chehab /* Logitech Quickcam OEM Dell Notebook */ 26290c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26300c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26310c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 26320c0d06caSMauro Carvalho Chehab .idProduct = 0x08c6, 26330c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 26340c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26350c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 26360c0d06caSMauro Carvalho Chehab /* Logitech Quickcam OEM Cisco VT Camera II */ 26370c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26380c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26390c0d06caSMauro Carvalho Chehab .idVendor = 0x046d, 26400c0d06caSMauro Carvalho Chehab .idProduct = 0x08c7, 26410c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 26420c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26430c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 264417e1319fSWilliam Manley /* Logitech HD Pro Webcam C920 */ 264517e1319fSWilliam Manley { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 264617e1319fSWilliam Manley | USB_DEVICE_ID_MATCH_INT_INFO, 264717e1319fSWilliam Manley .idVendor = 0x046d, 264817e1319fSWilliam Manley .idProduct = 0x082d, 264917e1319fSWilliam Manley .bInterfaceClass = USB_CLASS_VIDEO, 265017e1319fSWilliam Manley .bInterfaceSubClass = 1, 265117e1319fSWilliam Manley .bInterfaceProtocol = 0, 26526dbe1b76SOleksandr Natalenko .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT 26536dbe1b76SOleksandr Natalenko | UVC_QUIRK_INVALID_DEVICE_SOF) }, 26547b0155feSRicardo Ribalda /* Logitech HD Pro Webcam C922 */ 26557b0155feSRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26567b0155feSRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 26577b0155feSRicardo Ribalda .idVendor = 0x046d, 26587b0155feSRicardo Ribalda .idProduct = 0x085c, 26597b0155feSRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 26607b0155feSRicardo Ribalda .bInterfaceSubClass = 1, 26617b0155feSRicardo Ribalda .bInterfaceProtocol = 0, 26627b0155feSRicardo Ribalda .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_INVALID_DEVICE_SOF) }, 26634b3421c2SRicardo Ribalda /* Logitech Rally Bar Huddle */ 26644b3421c2SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26654b3421c2SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 26664b3421c2SRicardo Ribalda .idVendor = 0x046d, 26674b3421c2SRicardo Ribalda .idProduct = 0x087c, 26684b3421c2SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 26694b3421c2SRicardo Ribalda .bInterfaceSubClass = 1, 26704b3421c2SRicardo Ribalda .bInterfaceProtocol = 0, 26714b3421c2SRicardo Ribalda .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_NO_RESET_RESUME) }, 26724b3421c2SRicardo Ribalda /* Logitech Rally Bar */ 26734b3421c2SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26744b3421c2SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 26754b3421c2SRicardo Ribalda .idVendor = 0x046d, 26764b3421c2SRicardo Ribalda .idProduct = 0x089b, 26774b3421c2SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 26784b3421c2SRicardo Ribalda .bInterfaceSubClass = 1, 26794b3421c2SRicardo Ribalda .bInterfaceProtocol = 0, 26804b3421c2SRicardo Ribalda .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_NO_RESET_RESUME) }, 26814b3421c2SRicardo Ribalda /* Logitech Rally Bar Mini */ 26824b3421c2SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26834b3421c2SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 26844b3421c2SRicardo Ribalda .idVendor = 0x046d, 26854b3421c2SRicardo Ribalda .idProduct = 0x08d3, 26864b3421c2SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 26874b3421c2SRicardo Ribalda .bInterfaceSubClass = 1, 26884b3421c2SRicardo Ribalda .bInterfaceProtocol = 0, 26894b3421c2SRicardo Ribalda .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_NO_RESET_RESUME) }, 26900c0d06caSMauro Carvalho Chehab /* Chicony CNF7129 (Asus EEE 100HE) */ 26910c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 26920c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 26930c0d06caSMauro Carvalho Chehab .idVendor = 0x04f2, 26940c0d06caSMauro Carvalho Chehab .idProduct = 0xb071, 26950c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 26960c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 26970c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 269888d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTRICT_FRAME_RATE) }, 26999f22f959SRicardo Ribalda /* Chicony EasyCamera */ 27009f22f959SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27019f22f959SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 27029f22f959SRicardo Ribalda .idVendor = 0x04f2, 2703332a2235SRicardo Ribalda .idProduct = 0xb5eb, 2704332a2235SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 2705332a2235SRicardo Ribalda .bInterfaceSubClass = 1, 2706332a2235SRicardo Ribalda .bInterfaceProtocol = 0, 2707332a2235SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 2708051400c3SRicardo Ribalda /* Chicony Electronics Co., Ltd Integrated Camera */ 2709051400c3SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2710051400c3SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 2711051400c3SRicardo Ribalda .idVendor = 0x04f2, 2712051400c3SRicardo Ribalda .idProduct = 0xb67c, 2713051400c3SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 2714051400c3SRicardo Ribalda .bInterfaceSubClass = 1, 2715051400c3SRicardo Ribalda .bInterfaceProtocol = UVC_PC_PROTOCOL_15, 2716051400c3SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 }, 2717332a2235SRicardo Ribalda /* Chicony EasyCamera */ 2718332a2235SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2719332a2235SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 2720332a2235SRicardo Ribalda .idVendor = 0x04f2, 27219f22f959SRicardo Ribalda .idProduct = 0xb6ba, 27229f22f959SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 27239f22f959SRicardo Ribalda .bInterfaceSubClass = 1, 27249f22f959SRicardo Ribalda .bInterfaceProtocol = 0, 27259f22f959SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 27269f22f959SRicardo Ribalda /* Chicony EasyCamera */ 27279f22f959SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27289f22f959SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 27299f22f959SRicardo Ribalda .idVendor = 0x04f2, 27309f22f959SRicardo Ribalda .idProduct = 0xb746, 27319f22f959SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 27329f22f959SRicardo Ribalda .bInterfaceSubClass = 1, 27339f22f959SRicardo Ribalda .bInterfaceProtocol = 0, 27349f22f959SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 27350c0d06caSMauro Carvalho Chehab /* Alcor Micro AU3820 (Future Boy PC USB Webcam) */ 27360c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27370c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27380c0d06caSMauro Carvalho Chehab .idVendor = 0x058f, 27390c0d06caSMauro Carvalho Chehab .idProduct = 0x3820, 27400c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27410c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27420c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27433bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 27440c0d06caSMauro Carvalho Chehab /* Dell XPS m1530 */ 27450c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27460c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27470c0d06caSMauro Carvalho Chehab .idVendor = 0x05a9, 27480c0d06caSMauro Carvalho Chehab .idProduct = 0x2640, 27490c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27500c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27510c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 27523bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 275389e0f248SJoseph Salisbury /* Dell SP2008WFP Monitor */ 275489e0f248SJoseph Salisbury { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 275589e0f248SJoseph Salisbury | USB_DEVICE_ID_MATCH_INT_INFO, 275689e0f248SJoseph Salisbury .idVendor = 0x05a9, 275789e0f248SJoseph Salisbury .idProduct = 0x2641, 275889e0f248SJoseph Salisbury .bInterfaceClass = USB_CLASS_VIDEO, 275989e0f248SJoseph Salisbury .bInterfaceSubClass = 1, 276089e0f248SJoseph Salisbury .bInterfaceProtocol = 0, 27613bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 2762c2a273b2SJoseph Salisbury /* Dell Alienware X51 */ 2763c2a273b2SJoseph Salisbury { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2764c2a273b2SJoseph Salisbury | USB_DEVICE_ID_MATCH_INT_INFO, 2765c2a273b2SJoseph Salisbury .idVendor = 0x05a9, 2766c2a273b2SJoseph Salisbury .idProduct = 0x2643, 2767c2a273b2SJoseph Salisbury .bInterfaceClass = USB_CLASS_VIDEO, 2768c2a273b2SJoseph Salisbury .bInterfaceSubClass = 1, 2769c2a273b2SJoseph Salisbury .bInterfaceProtocol = 0, 27703bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 2771afcf44c7SKamal Mostafa /* Dell Studio Hybrid 140g (OmniVision webcam) */ 2772afcf44c7SKamal Mostafa { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2773afcf44c7SKamal Mostafa | USB_DEVICE_ID_MATCH_INT_INFO, 2774afcf44c7SKamal Mostafa .idVendor = 0x05a9, 2775afcf44c7SKamal Mostafa .idProduct = 0x264a, 2776afcf44c7SKamal Mostafa .bInterfaceClass = USB_CLASS_VIDEO, 2777afcf44c7SKamal Mostafa .bInterfaceSubClass = 1, 2778afcf44c7SKamal Mostafa .bInterfaceProtocol = 0, 27793bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 278062ea864fSPaul Fertser /* Dell XPS M1330 (OmniVision OV7670 webcam) */ 278162ea864fSPaul Fertser { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 278262ea864fSPaul Fertser | USB_DEVICE_ID_MATCH_INT_INFO, 278362ea864fSPaul Fertser .idVendor = 0x05a9, 278462ea864fSPaul Fertser .idProduct = 0x7670, 278562ea864fSPaul Fertser .bInterfaceClass = USB_CLASS_VIDEO, 278662ea864fSPaul Fertser .bInterfaceSubClass = 1, 278762ea864fSPaul Fertser .bInterfaceProtocol = 0, 27883bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 27890c0d06caSMauro Carvalho Chehab /* Apple Built-In iSight */ 27900c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 27910c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 27920c0d06caSMauro Carvalho Chehab .idVendor = 0x05ac, 27930c0d06caSMauro Carvalho Chehab .idProduct = 0x8501, 27940c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 27950c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 27960c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 279788d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 27983bc85817SGuennadi Liakhovetski | UVC_QUIRK_BUILTIN_ISIGHT) }, 279953c26454SPaul Pawlowski /* Apple FaceTime HD Camera (Built-In) */ 280053c26454SPaul Pawlowski { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 280153c26454SPaul Pawlowski | USB_DEVICE_ID_MATCH_INT_INFO, 280253c26454SPaul Pawlowski .idVendor = 0x05ac, 280353c26454SPaul Pawlowski .idProduct = 0x8514, 280453c26454SPaul Pawlowski .bInterfaceClass = USB_CLASS_VIDEO, 280553c26454SPaul Pawlowski .bInterfaceSubClass = 1, 280653c26454SPaul Pawlowski .bInterfaceProtocol = 0, 280753c26454SPaul Pawlowski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 28087b848ed6SDaniel Roschka /* Apple Built-In iSight via iBridge */ 28097b848ed6SDaniel Roschka { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28107b848ed6SDaniel Roschka | USB_DEVICE_ID_MATCH_INT_INFO, 28117b848ed6SDaniel Roschka .idVendor = 0x05ac, 28127b848ed6SDaniel Roschka .idProduct = 0x8600, 28137b848ed6SDaniel Roschka .bInterfaceClass = USB_CLASS_VIDEO, 28147b848ed6SDaniel Roschka .bInterfaceSubClass = 1, 28157b848ed6SDaniel Roschka .bInterfaceProtocol = 0, 28163bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 28170c0d06caSMauro Carvalho Chehab /* Foxlink ("HP Webcam" on HP Mini 5103) */ 28180c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28190c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28200c0d06caSMauro Carvalho Chehab .idVendor = 0x05c8, 28210c0d06caSMauro Carvalho Chehab .idProduct = 0x0403, 28220c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28230c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28240c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 28253bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 28260c0d06caSMauro Carvalho Chehab /* Genesys Logic USB 2.0 PC Camera */ 28270c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28280c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28290c0d06caSMauro Carvalho Chehab .idVendor = 0x05e3, 28300c0d06caSMauro Carvalho Chehab .idProduct = 0x0505, 28310c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28320c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28330c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 28343bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 28350c0d06caSMauro Carvalho Chehab /* Hercules Classic Silver */ 28360c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28370c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28380c0d06caSMauro Carvalho Chehab .idVendor = 0x06f8, 28390c0d06caSMauro Carvalho Chehab .idProduct = 0x300c, 28400c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28410c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28420c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 28433bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 28440c0d06caSMauro Carvalho Chehab /* ViMicro Vega */ 28450c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28460c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28470c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 28480c0d06caSMauro Carvalho Chehab .idProduct = 0x332d, 28490c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28500c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28510c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 28523bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 28530c0d06caSMauro Carvalho Chehab /* ViMicro - Minoru3D */ 28540c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28550c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28560c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 28570c0d06caSMauro Carvalho Chehab .idProduct = 0x3410, 28580c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28590c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28600c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 28613bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 28620c0d06caSMauro Carvalho Chehab /* ViMicro Venus - Minoru3D */ 28630c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28640c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28650c0d06caSMauro Carvalho Chehab .idVendor = 0x0ac8, 28660c0d06caSMauro Carvalho Chehab .idProduct = 0x3420, 28670c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28680c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28690c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 28703bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 28710c0d06caSMauro Carvalho Chehab /* Ophir Optronics - SPCAM 620U */ 28720c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28730c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28740c0d06caSMauro Carvalho Chehab .idVendor = 0x0bd3, 28750c0d06caSMauro Carvalho Chehab .idProduct = 0x0555, 28760c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28770c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28780c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 28793bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 28800c0d06caSMauro Carvalho Chehab /* MT6227 */ 28810c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28820c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28830c0d06caSMauro Carvalho Chehab .idVendor = 0x0e8d, 28840c0d06caSMauro Carvalho Chehab .idProduct = 0x0004, 28850c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28860c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28870c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 288888d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 28893bc85817SGuennadi Liakhovetski | UVC_QUIRK_PROBE_DEF) }, 28900c0d06caSMauro Carvalho Chehab /* IMC Networks (Medion Akoya) */ 28910c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 28920c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 28930c0d06caSMauro Carvalho Chehab .idVendor = 0x13d3, 28940c0d06caSMauro Carvalho Chehab .idProduct = 0x5103, 28950c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 28960c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 28970c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 28983bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 28990c0d06caSMauro Carvalho Chehab /* JMicron USB2.0 XGA WebCam */ 29000c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29010c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 29020c0d06caSMauro Carvalho Chehab .idVendor = 0x152d, 29030c0d06caSMauro Carvalho Chehab .idProduct = 0x0310, 29040c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 29050c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 29060c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 29073bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 29080c0d06caSMauro Carvalho Chehab /* Syntek (HP Spartan) */ 29090c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29100c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 29110c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 29120c0d06caSMauro Carvalho Chehab .idProduct = 0x5212, 29130c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 29140c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 29150c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 29163bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 29170c0d06caSMauro Carvalho Chehab /* Syntek (Samsung Q310) */ 29180c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29190c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 29200c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 29210c0d06caSMauro Carvalho Chehab .idProduct = 0x5931, 29220c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 29230c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 29240c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 29253bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 29260c0d06caSMauro Carvalho Chehab /* Syntek (Packard Bell EasyNote MX52 */ 29270c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29280c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 29290c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 29300c0d06caSMauro Carvalho Chehab .idProduct = 0x8a12, 29310c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 29320c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 29330c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 29343bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 29350c0d06caSMauro Carvalho Chehab /* Syntek (Asus F9SG) */ 29360c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29370c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 29380c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 29390c0d06caSMauro Carvalho Chehab .idProduct = 0x8a31, 29400c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 29410c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 29420c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 29433bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 29440c0d06caSMauro Carvalho Chehab /* Syntek (Asus U3S) */ 29450c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29460c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 29470c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 29480c0d06caSMauro Carvalho Chehab .idProduct = 0x8a33, 29490c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 29500c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 29510c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 29523bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 29530c0d06caSMauro Carvalho Chehab /* Syntek (JAOtech Smart Terminal) */ 29540c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29550c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 29560c0d06caSMauro Carvalho Chehab .idVendor = 0x174f, 29570c0d06caSMauro Carvalho Chehab .idProduct = 0x8a34, 29580c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 29590c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 29600c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 29613bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 29620c0d06caSMauro Carvalho Chehab /* Miricle 307K */ 29630c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29640c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 29650c0d06caSMauro Carvalho Chehab .idVendor = 0x17dc, 29660c0d06caSMauro Carvalho Chehab .idProduct = 0x0202, 29670c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 29680c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 29690c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 29703bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 29710c0d06caSMauro Carvalho Chehab /* Lenovo Thinkpad SL400/SL500 */ 29720c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29730c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 29740c0d06caSMauro Carvalho Chehab .idVendor = 0x17ef, 29750c0d06caSMauro Carvalho Chehab .idProduct = 0x480b, 29760c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 29770c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 29780c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 29793bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 29800c0d06caSMauro Carvalho Chehab /* Aveo Technology USB 2.0 Camera */ 29810c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 29820c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 29830c0d06caSMauro Carvalho Chehab .idVendor = 0x1871, 29840c0d06caSMauro Carvalho Chehab .idProduct = 0x0306, 29850c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 29860c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 29870c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 298888d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 29893bc85817SGuennadi Liakhovetski | UVC_QUIRK_PROBE_EXTRAFIELDS) }, 2990fe652471SLaurent Pinchart /* Aveo Technology USB 2.0 Camera (Tasco USB Microscope) */ 2991fe652471SLaurent Pinchart { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 2992fe652471SLaurent Pinchart | USB_DEVICE_ID_MATCH_INT_INFO, 2993fe652471SLaurent Pinchart .idVendor = 0x1871, 2994fe652471SLaurent Pinchart .idProduct = 0x0516, 2995fe652471SLaurent Pinchart .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 2996fe652471SLaurent Pinchart .bInterfaceSubClass = 1, 2997fe652471SLaurent Pinchart .bInterfaceProtocol = 0 }, 29980c0d06caSMauro Carvalho Chehab /* Ecamm Pico iMage */ 29990c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30000c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30010c0d06caSMauro Carvalho Chehab .idVendor = 0x18cd, 30020c0d06caSMauro Carvalho Chehab .idProduct = 0xcafe, 30030c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30040c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30050c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 300688d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_EXTRAFIELDS) }, 30070c0d06caSMauro Carvalho Chehab /* Manta MM-353 Plako */ 30080c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30090c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30100c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 30110c0d06caSMauro Carvalho Chehab .idProduct = 0x3188, 30120c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30130c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30140c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 30153bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 30160c0d06caSMauro Carvalho Chehab /* FSC WebCam V30S */ 30170c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30180c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30190c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 30200c0d06caSMauro Carvalho Chehab .idProduct = 0x3288, 30210c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30220c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30230c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 30243bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 30250c0d06caSMauro Carvalho Chehab /* Arkmicro unbranded */ 30260c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30270c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30280c0d06caSMauro Carvalho Chehab .idVendor = 0x18ec, 30290c0d06caSMauro Carvalho Chehab .idProduct = 0x3290, 30300c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30310c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30320c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 30333bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 30340c0d06caSMauro Carvalho Chehab /* The Imaging Source USB CCD cameras */ 30350c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30360c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30370c0d06caSMauro Carvalho Chehab .idVendor = 0x199e, 30380c0d06caSMauro Carvalho Chehab .idProduct = 0x8102, 30390c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 30400c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30410c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0 }, 30420c0d06caSMauro Carvalho Chehab /* Bodelin ProScopeHR */ 30430c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30440c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_DEV_HI 30450c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30460c0d06caSMauro Carvalho Chehab .idVendor = 0x19ab, 30470c0d06caSMauro Carvalho Chehab .idProduct = 0x1000, 30480c0d06caSMauro Carvalho Chehab .bcdDevice_hi = 0x0126, 30490c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30500c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30510c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 305288d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_STATUS_INTERVAL) }, 30530c0d06caSMauro Carvalho Chehab /* MSI StarCam 370i */ 30540c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30550c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30560c0d06caSMauro Carvalho Chehab .idVendor = 0x1b3b, 30570c0d06caSMauro Carvalho Chehab .idProduct = 0x2951, 30580c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30590c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30600c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 30613bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 3062589266bdSNeil Armstrong /* Generalplus Technology Inc. 808 Camera */ 3063589266bdSNeil Armstrong { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3064589266bdSNeil Armstrong | USB_DEVICE_ID_MATCH_INT_INFO, 3065589266bdSNeil Armstrong .idVendor = 0x1b3f, 3066589266bdSNeil Armstrong .idProduct = 0x2002, 3067589266bdSNeil Armstrong .bInterfaceClass = USB_CLASS_VIDEO, 3068589266bdSNeil Armstrong .bInterfaceSubClass = 1, 3069589266bdSNeil Armstrong .bInterfaceProtocol = 0, 3070589266bdSNeil Armstrong .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 3071b400b6f2SLaurent Pinchart /* Shenzhen Aoni Electronic Co.,Ltd 2K FHD camera */ 3072b400b6f2SLaurent Pinchart { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3073b400b6f2SLaurent Pinchart | USB_DEVICE_ID_MATCH_INT_INFO, 3074b400b6f2SLaurent Pinchart .idVendor = 0x1bcf, 3075b400b6f2SLaurent Pinchart .idProduct = 0x0b40, 3076b400b6f2SLaurent Pinchart .bInterfaceClass = USB_CLASS_VIDEO, 3077b400b6f2SLaurent Pinchart .bInterfaceSubClass = 1, 3078b400b6f2SLaurent Pinchart .bInterfaceProtocol = 0, 3079b400b6f2SLaurent Pinchart .driver_info = (kernel_ulong_t)&(const struct uvc_device_info){ 3080b400b6f2SLaurent Pinchart .uvc_version = 0x010a, 3081b400b6f2SLaurent Pinchart } }, 30820c0d06caSMauro Carvalho Chehab /* SiGma Micro USB Web Camera */ 30830c0d06caSMauro Carvalho Chehab { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 30840c0d06caSMauro Carvalho Chehab | USB_DEVICE_ID_MATCH_INT_INFO, 30850c0d06caSMauro Carvalho Chehab .idVendor = 0x1c4f, 30860c0d06caSMauro Carvalho Chehab .idProduct = 0x3000, 30870c0d06caSMauro Carvalho Chehab .bInterfaceClass = USB_CLASS_VIDEO, 30880c0d06caSMauro Carvalho Chehab .bInterfaceSubClass = 1, 30890c0d06caSMauro Carvalho Chehab .bInterfaceProtocol = 0, 309088d8034cSGuennadi Liakhovetski .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 30913bc85817SGuennadi Liakhovetski | UVC_QUIRK_IGNORE_SELECTOR_UNIT) }, 309231f9b4a2SDavid Given /* NXP Semiconductors IR VIDEO */ 309331f9b4a2SDavid Given { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 309431f9b4a2SDavid Given | USB_DEVICE_ID_MATCH_INT_INFO, 309531f9b4a2SDavid Given .idVendor = 0x1fc9, 309631f9b4a2SDavid Given .idProduct = 0x009b, 309731f9b4a2SDavid Given .bInterfaceClass = USB_CLASS_VIDEO, 309831f9b4a2SDavid Given .bInterfaceSubClass = 1, 309931f9b4a2SDavid Given .bInterfaceProtocol = 0, 310031f9b4a2SDavid Given .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 3101e1b78a33SPhilipp Zabel /* Oculus VR Positional Tracker DK2 */ 3102e1b78a33SPhilipp Zabel { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3103e1b78a33SPhilipp Zabel | USB_DEVICE_ID_MATCH_INT_INFO, 3104e1b78a33SPhilipp Zabel .idVendor = 0x2833, 3105e1b78a33SPhilipp Zabel .idProduct = 0x0201, 3106e1b78a33SPhilipp Zabel .bInterfaceClass = USB_CLASS_VIDEO, 3107e1b78a33SPhilipp Zabel .bInterfaceSubClass = 1, 3108e1b78a33SPhilipp Zabel .bInterfaceProtocol = 0, 31093bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_force_y8 }, 311003c47aaeSPhilipp Zabel /* Oculus VR Rift Sensor */ 311103c47aaeSPhilipp Zabel { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 311203c47aaeSPhilipp Zabel | USB_DEVICE_ID_MATCH_INT_INFO, 311303c47aaeSPhilipp Zabel .idVendor = 0x2833, 311403c47aaeSPhilipp Zabel .idProduct = 0x0211, 311503c47aaeSPhilipp Zabel .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 311603c47aaeSPhilipp Zabel .bInterfaceSubClass = 1, 311703c47aaeSPhilipp Zabel .bInterfaceProtocol = 0, 31183bc85817SGuennadi Liakhovetski .driver_info = (kernel_ulong_t)&uvc_quirk_force_y8 }, 31191dd2e8f9SSergey Zakharchenko /* GEO Semiconductor GC6500 */ 31201dd2e8f9SSergey Zakharchenko { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 31211dd2e8f9SSergey Zakharchenko | USB_DEVICE_ID_MATCH_INT_INFO, 31221dd2e8f9SSergey Zakharchenko .idVendor = 0x29fe, 31231dd2e8f9SSergey Zakharchenko .idProduct = 0x4d53, 31241dd2e8f9SSergey Zakharchenko .bInterfaceClass = USB_CLASS_VIDEO, 31251dd2e8f9SSergey Zakharchenko .bInterfaceSubClass = 1, 31261dd2e8f9SSergey Zakharchenko .bInterfaceProtocol = 0, 31271dd2e8f9SSergey Zakharchenko .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_FORCE_BPP) }, 31285193d26aSRicardo Ribalda /* SunplusIT Inc HD Camera */ 31295193d26aSRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 31305193d26aSRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 31315193d26aSRicardo Ribalda .idVendor = 0x2b7e, 31325193d26aSRicardo Ribalda .idProduct = 0xb752, 31335193d26aSRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 31345193d26aSRicardo Ribalda .bInterfaceSubClass = 1, 31355193d26aSRicardo Ribalda .bInterfaceProtocol = UVC_PC_PROTOCOL_15, 31365193d26aSRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 }, 3137991df394SRicardo Ribalda /* Insta360 Link */ 3138991df394SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3139991df394SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 3140991df394SRicardo Ribalda .idVendor = 0x2e1a, 3141991df394SRicardo Ribalda .idProduct = 0x4c01, 3142991df394SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 3143991df394SRicardo Ribalda .bInterfaceSubClass = 1, 3144991df394SRicardo Ribalda .bInterfaceProtocol = 0, 3145991df394SRicardo Ribalda .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_DISABLE_AUTOSUSPEND) }, 3146a7c28150SRicardo Ribalda /* Lenovo Integrated Camera */ 3147a7c28150SRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3148a7c28150SRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 3149a7c28150SRicardo Ribalda .idVendor = 0x30c9, 3150a7c28150SRicardo Ribalda .idProduct = 0x0093, 3151a7c28150SRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 3152a7c28150SRicardo Ribalda .bInterfaceSubClass = 1, 3153a7c28150SRicardo Ribalda .bInterfaceProtocol = UVC_PC_PROTOCOL_15, 3154a7c28150SRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 }, 3155101418b3Shuanglei /* Sonix Technology USB 2.0 Camera */ 3156101418b3Shuanglei { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3157101418b3Shuanglei | USB_DEVICE_ID_MATCH_INT_INFO, 3158101418b3Shuanglei .idVendor = 0x3277, 3159101418b3Shuanglei .idProduct = 0x0072, 3160101418b3Shuanglei .bInterfaceClass = USB_CLASS_VIDEO, 3161101418b3Shuanglei .bInterfaceSubClass = 1, 3162101418b3Shuanglei .bInterfaceProtocol = 0, 3163101418b3Shuanglei .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 3164eff1e24cSRicardo Ribalda /* Acer EasyCamera */ 3165eff1e24cSRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3166eff1e24cSRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 3167eff1e24cSRicardo Ribalda .idVendor = 0x5986, 3168eff1e24cSRicardo Ribalda .idProduct = 0x1172, 3169eff1e24cSRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 3170eff1e24cSRicardo Ribalda .bInterfaceSubClass = 1, 3171eff1e24cSRicardo Ribalda .bInterfaceProtocol = 0, 3172eff1e24cSRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 317381e78a6fSRicardo Ribalda /* Acer EasyCamera */ 317481e78a6fSRicardo Ribalda { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 317581e78a6fSRicardo Ribalda | USB_DEVICE_ID_MATCH_INT_INFO, 317681e78a6fSRicardo Ribalda .idVendor = 0x5986, 317781e78a6fSRicardo Ribalda .idProduct = 0x1180, 317881e78a6fSRicardo Ribalda .bInterfaceClass = USB_CLASS_VIDEO, 317981e78a6fSRicardo Ribalda .bInterfaceSubClass = 1, 318081e78a6fSRicardo Ribalda .bInterfaceProtocol = 0, 318181e78a6fSRicardo Ribalda .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 3182e33ae66aSDmitry Perchanov /* Intel D410/ASR depth camera */ 3183e33ae66aSDmitry Perchanov { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3184e33ae66aSDmitry Perchanov | USB_DEVICE_ID_MATCH_INT_INFO, 3185e33ae66aSDmitry Perchanov .idVendor = 0x8086, 3186e33ae66aSDmitry Perchanov .idProduct = 0x0ad2, 3187e33ae66aSDmitry Perchanov .bInterfaceClass = USB_CLASS_VIDEO, 3188e33ae66aSDmitry Perchanov .bInterfaceSubClass = 1, 3189e33ae66aSDmitry Perchanov .bInterfaceProtocol = 0, 3190e33ae66aSDmitry Perchanov .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 3191e33ae66aSDmitry Perchanov /* Intel D415/ASRC depth camera */ 3192e33ae66aSDmitry Perchanov { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3193e33ae66aSDmitry Perchanov | USB_DEVICE_ID_MATCH_INT_INFO, 3194e33ae66aSDmitry Perchanov .idVendor = 0x8086, 3195e33ae66aSDmitry Perchanov .idProduct = 0x0ad3, 3196e33ae66aSDmitry Perchanov .bInterfaceClass = USB_CLASS_VIDEO, 3197e33ae66aSDmitry Perchanov .bInterfaceSubClass = 1, 3198e33ae66aSDmitry Perchanov .bInterfaceProtocol = 0, 3199e33ae66aSDmitry Perchanov .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 3200e33ae66aSDmitry Perchanov /* Intel D430/AWG depth camera */ 3201e33ae66aSDmitry Perchanov { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3202e33ae66aSDmitry Perchanov | USB_DEVICE_ID_MATCH_INT_INFO, 3203e33ae66aSDmitry Perchanov .idVendor = 0x8086, 3204e33ae66aSDmitry Perchanov .idProduct = 0x0ad4, 3205e33ae66aSDmitry Perchanov .bInterfaceClass = USB_CLASS_VIDEO, 3206e33ae66aSDmitry Perchanov .bInterfaceSubClass = 1, 3207e33ae66aSDmitry Perchanov .bInterfaceProtocol = 0, 3208e33ae66aSDmitry Perchanov .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 32096ea0d588SGuennadi Liakhovetski /* Intel RealSense D4M */ 32106ea0d588SGuennadi Liakhovetski { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 32116ea0d588SGuennadi Liakhovetski | USB_DEVICE_ID_MATCH_INT_INFO, 32126ea0d588SGuennadi Liakhovetski .idVendor = 0x8086, 32136ea0d588SGuennadi Liakhovetski .idProduct = 0x0b03, 32146ea0d588SGuennadi Liakhovetski .bInterfaceClass = USB_CLASS_VIDEO, 32156ea0d588SGuennadi Liakhovetski .bInterfaceSubClass = 1, 32166ea0d588SGuennadi Liakhovetski .bInterfaceProtocol = 0, 32176ea0d588SGuennadi Liakhovetski .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 3218e33ae66aSDmitry Perchanov /* Intel D435/AWGC depth camera */ 3219e33ae66aSDmitry Perchanov { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3220e33ae66aSDmitry Perchanov | USB_DEVICE_ID_MATCH_INT_INFO, 3221e33ae66aSDmitry Perchanov .idVendor = 0x8086, 3222e33ae66aSDmitry Perchanov .idProduct = 0x0b07, 3223e33ae66aSDmitry Perchanov .bInterfaceClass = USB_CLASS_VIDEO, 3224e33ae66aSDmitry Perchanov .bInterfaceSubClass = 1, 3225e33ae66aSDmitry Perchanov .bInterfaceProtocol = 0, 3226e33ae66aSDmitry Perchanov .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 3227e33ae66aSDmitry Perchanov /* Intel D435i depth camera */ 3228e33ae66aSDmitry Perchanov { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3229e33ae66aSDmitry Perchanov | USB_DEVICE_ID_MATCH_INT_INFO, 3230e33ae66aSDmitry Perchanov .idVendor = 0x8086, 3231e33ae66aSDmitry Perchanov .idProduct = 0x0b3a, 3232e33ae66aSDmitry Perchanov .bInterfaceClass = USB_CLASS_VIDEO, 3233e33ae66aSDmitry Perchanov .bInterfaceSubClass = 1, 3234e33ae66aSDmitry Perchanov .bInterfaceProtocol = 0, 3235e33ae66aSDmitry Perchanov .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 3236e33ae66aSDmitry Perchanov /* Intel D405 Depth Camera */ 3237e33ae66aSDmitry Perchanov { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3238e33ae66aSDmitry Perchanov | USB_DEVICE_ID_MATCH_INT_INFO, 3239e33ae66aSDmitry Perchanov .idVendor = 0x8086, 3240e33ae66aSDmitry Perchanov .idProduct = 0x0b5b, 3241e33ae66aSDmitry Perchanov .bInterfaceClass = USB_CLASS_VIDEO, 3242e33ae66aSDmitry Perchanov .bInterfaceSubClass = 1, 3243e33ae66aSDmitry Perchanov .bInterfaceProtocol = 0, 3244e33ae66aSDmitry Perchanov .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 3245e33ae66aSDmitry Perchanov /* Intel D455 Depth Camera */ 3246e33ae66aSDmitry Perchanov { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3247e33ae66aSDmitry Perchanov | USB_DEVICE_ID_MATCH_INT_INFO, 3248e33ae66aSDmitry Perchanov .idVendor = 0x8086, 3249e33ae66aSDmitry Perchanov .idProduct = 0x0b5c, 3250e33ae66aSDmitry Perchanov .bInterfaceClass = USB_CLASS_VIDEO, 3251e33ae66aSDmitry Perchanov .bInterfaceSubClass = 1, 3252e33ae66aSDmitry Perchanov .bInterfaceProtocol = 0, 3253e33ae66aSDmitry Perchanov .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 3254f20f3416SDmitry Perchanov /* Intel D421 Depth Module */ 3255f20f3416SDmitry Perchanov { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 3256f20f3416SDmitry Perchanov | USB_DEVICE_ID_MATCH_INT_INFO, 3257f20f3416SDmitry Perchanov .idVendor = 0x8086, 3258f20f3416SDmitry Perchanov .idProduct = 0x1155, 3259f20f3416SDmitry Perchanov .bInterfaceClass = USB_CLASS_VIDEO, 3260f20f3416SDmitry Perchanov .bInterfaceSubClass = 1, 3261f20f3416SDmitry Perchanov .bInterfaceProtocol = 0, 3262f20f3416SDmitry Perchanov .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 32630c0d06caSMauro Carvalho Chehab /* Generic USB Video Class */ 32648afe97beSLaurent Pinchart { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_UNDEFINED) }, 32658afe97beSLaurent Pinchart { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_15) }, 32660c0d06caSMauro Carvalho Chehab {} 32670c0d06caSMauro Carvalho Chehab }; 32680c0d06caSMauro Carvalho Chehab 32690c0d06caSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, uvc_ids); 32700c0d06caSMauro Carvalho Chehab 32710c0d06caSMauro Carvalho Chehab struct uvc_driver uvc_driver = { 32720c0d06caSMauro Carvalho Chehab .driver = { 32730c0d06caSMauro Carvalho Chehab .name = "uvcvideo", 32740c0d06caSMauro Carvalho Chehab .probe = uvc_probe, 32750c0d06caSMauro Carvalho Chehab .disconnect = uvc_disconnect, 32760c0d06caSMauro Carvalho Chehab .suspend = uvc_suspend, 32770c0d06caSMauro Carvalho Chehab .resume = uvc_resume, 32780c0d06caSMauro Carvalho Chehab .reset_resume = uvc_reset_resume, 32790c0d06caSMauro Carvalho Chehab .id_table = uvc_ids, 32800c0d06caSMauro Carvalho Chehab .supports_autosuspend = 1, 32810c0d06caSMauro Carvalho Chehab }, 32820c0d06caSMauro Carvalho Chehab }; 32830c0d06caSMauro Carvalho Chehab 32840c0d06caSMauro Carvalho Chehab static int __init uvc_init(void) 32850c0d06caSMauro Carvalho Chehab { 32860c0d06caSMauro Carvalho Chehab int ret; 32870c0d06caSMauro Carvalho Chehab 32880c0d06caSMauro Carvalho Chehab uvc_debugfs_init(); 32890c0d06caSMauro Carvalho Chehab 32900c0d06caSMauro Carvalho Chehab ret = usb_register(&uvc_driver.driver); 32910c0d06caSMauro Carvalho Chehab if (ret < 0) { 32920c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup(); 32930c0d06caSMauro Carvalho Chehab return ret; 32940c0d06caSMauro Carvalho Chehab } 32950c0d06caSMauro Carvalho Chehab 32960c0d06caSMauro Carvalho Chehab return 0; 32970c0d06caSMauro Carvalho Chehab } 32980c0d06caSMauro Carvalho Chehab 32990c0d06caSMauro Carvalho Chehab static void __exit uvc_cleanup(void) 33000c0d06caSMauro Carvalho Chehab { 33010c0d06caSMauro Carvalho Chehab usb_deregister(&uvc_driver.driver); 33020c0d06caSMauro Carvalho Chehab uvc_debugfs_cleanup(); 33030c0d06caSMauro Carvalho Chehab } 33040c0d06caSMauro Carvalho Chehab 33050c0d06caSMauro Carvalho Chehab module_init(uvc_init); 33060c0d06caSMauro Carvalho Chehab module_exit(uvc_cleanup); 33070c0d06caSMauro Carvalho Chehab 33080c0d06caSMauro Carvalho Chehab MODULE_AUTHOR(DRIVER_AUTHOR); 33090c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION(DRIVER_DESC); 33100c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 33110c0d06caSMauro Carvalho Chehab MODULE_VERSION(DRIVER_VERSION); 33120c0d06caSMauro Carvalho Chehab 3313