162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * uvc_driver.c -- USB Video Class driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2005-2010 662306a36Sopenharmony_ci * Laurent Pinchart (laurent.pinchart@ideasonboard.com) 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/atomic.h> 1062306a36Sopenharmony_ci#include <linux/bits.h> 1162306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/list.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/usb.h> 1762306a36Sopenharmony_ci#include <linux/usb/uvc.h> 1862306a36Sopenharmony_ci#include <linux/videodev2.h> 1962306a36Sopenharmony_ci#include <linux/vmalloc.h> 2062306a36Sopenharmony_ci#include <linux/wait.h> 2162306a36Sopenharmony_ci#include <asm/unaligned.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <media/v4l2-common.h> 2462306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "uvcvideo.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define DRIVER_AUTHOR "Laurent Pinchart " \ 2962306a36Sopenharmony_ci "<laurent.pinchart@ideasonboard.com>" 3062306a36Sopenharmony_ci#define DRIVER_DESC "USB Video Class driver" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ciunsigned int uvc_clock_param = CLOCK_MONOTONIC; 3362306a36Sopenharmony_ciunsigned int uvc_hw_timestamps_param; 3462306a36Sopenharmony_ciunsigned int uvc_no_drop_param; 3562306a36Sopenharmony_cistatic unsigned int uvc_quirks_param = -1; 3662306a36Sopenharmony_ciunsigned int uvc_dbg_param; 3762306a36Sopenharmony_ciunsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* ------------------------------------------------------------------------ 4062306a36Sopenharmony_ci * Utility functions 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts, 4462306a36Sopenharmony_ci u8 epaddr) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct usb_host_endpoint *ep; 4762306a36Sopenharmony_ci unsigned int i; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci for (i = 0; i < alts->desc.bNumEndpoints; ++i) { 5062306a36Sopenharmony_ci ep = &alts->endpoint[i]; 5162306a36Sopenharmony_ci if (ep->desc.bEndpointAddress == epaddr) 5262306a36Sopenharmony_ci return ep; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return NULL; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic enum v4l2_colorspace uvc_colorspace(const u8 primaries) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci static const enum v4l2_colorspace colorprimaries[] = { 6162306a36Sopenharmony_ci V4L2_COLORSPACE_SRGB, /* Unspecified */ 6262306a36Sopenharmony_ci V4L2_COLORSPACE_SRGB, 6362306a36Sopenharmony_ci V4L2_COLORSPACE_470_SYSTEM_M, 6462306a36Sopenharmony_ci V4L2_COLORSPACE_470_SYSTEM_BG, 6562306a36Sopenharmony_ci V4L2_COLORSPACE_SMPTE170M, 6662306a36Sopenharmony_ci V4L2_COLORSPACE_SMPTE240M, 6762306a36Sopenharmony_ci }; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (primaries < ARRAY_SIZE(colorprimaries)) 7062306a36Sopenharmony_ci return colorprimaries[primaries]; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return V4L2_COLORSPACE_SRGB; /* Reserved */ 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic enum v4l2_xfer_func uvc_xfer_func(const u8 transfer_characteristics) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci /* 7862306a36Sopenharmony_ci * V4L2 does not currently have definitions for all possible values of 7962306a36Sopenharmony_ci * UVC transfer characteristics. If v4l2_xfer_func is extended with new 8062306a36Sopenharmony_ci * values, the mapping below should be updated. 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * Substitutions are taken from the mapping given for 8362306a36Sopenharmony_ci * V4L2_XFER_FUNC_DEFAULT documented in videodev2.h. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci static const enum v4l2_xfer_func xfer_funcs[] = { 8662306a36Sopenharmony_ci V4L2_XFER_FUNC_DEFAULT, /* Unspecified */ 8762306a36Sopenharmony_ci V4L2_XFER_FUNC_709, 8862306a36Sopenharmony_ci V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 M */ 8962306a36Sopenharmony_ci V4L2_XFER_FUNC_709, /* Substitution for BT.470-2 B, G */ 9062306a36Sopenharmony_ci V4L2_XFER_FUNC_709, /* Substitution for SMPTE 170M */ 9162306a36Sopenharmony_ci V4L2_XFER_FUNC_SMPTE240M, 9262306a36Sopenharmony_ci V4L2_XFER_FUNC_NONE, 9362306a36Sopenharmony_ci V4L2_XFER_FUNC_SRGB, 9462306a36Sopenharmony_ci }; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (transfer_characteristics < ARRAY_SIZE(xfer_funcs)) 9762306a36Sopenharmony_ci return xfer_funcs[transfer_characteristics]; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return V4L2_XFER_FUNC_DEFAULT; /* Reserved */ 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic enum v4l2_ycbcr_encoding uvc_ycbcr_enc(const u8 matrix_coefficients) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci /* 10562306a36Sopenharmony_ci * V4L2 does not currently have definitions for all possible values of 10662306a36Sopenharmony_ci * UVC matrix coefficients. If v4l2_ycbcr_encoding is extended with new 10762306a36Sopenharmony_ci * values, the mapping below should be updated. 10862306a36Sopenharmony_ci * 10962306a36Sopenharmony_ci * Substitutions are taken from the mapping given for 11062306a36Sopenharmony_ci * V4L2_YCBCR_ENC_DEFAULT documented in videodev2.h. 11162306a36Sopenharmony_ci * 11262306a36Sopenharmony_ci * FCC is assumed to be close enough to 601. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci static const enum v4l2_ycbcr_encoding ycbcr_encs[] = { 11562306a36Sopenharmony_ci V4L2_YCBCR_ENC_DEFAULT, /* Unspecified */ 11662306a36Sopenharmony_ci V4L2_YCBCR_ENC_709, 11762306a36Sopenharmony_ci V4L2_YCBCR_ENC_601, /* Substitution for FCC */ 11862306a36Sopenharmony_ci V4L2_YCBCR_ENC_601, /* Substitution for BT.470-2 B, G */ 11962306a36Sopenharmony_ci V4L2_YCBCR_ENC_601, 12062306a36Sopenharmony_ci V4L2_YCBCR_ENC_SMPTE240M, 12162306a36Sopenharmony_ci }; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (matrix_coefficients < ARRAY_SIZE(ycbcr_encs)) 12462306a36Sopenharmony_ci return ycbcr_encs[matrix_coefficients]; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return V4L2_YCBCR_ENC_DEFAULT; /* Reserved */ 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* ------------------------------------------------------------------------ 13062306a36Sopenharmony_ci * Terminal and unit management 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistruct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct uvc_entity *entity; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci list_for_each_entry(entity, &dev->entities, list) { 13862306a36Sopenharmony_ci if (entity->id == id) 13962306a36Sopenharmony_ci return entity; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return NULL; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, 14662306a36Sopenharmony_ci int id, struct uvc_entity *entity) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci unsigned int i; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (entity == NULL) 15162306a36Sopenharmony_ci entity = list_entry(&dev->entities, struct uvc_entity, list); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci list_for_each_entry_continue(entity, &dev->entities, list) { 15462306a36Sopenharmony_ci for (i = 0; i < entity->bNrInPins; ++i) 15562306a36Sopenharmony_ci if (entity->baSourceID[i] == id) 15662306a36Sopenharmony_ci return entity; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return NULL; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct uvc_streaming *stream; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci list_for_each_entry(stream, &dev->streams, list) { 16762306a36Sopenharmony_ci if (stream->header.bTerminalLink == id) 16862306a36Sopenharmony_ci return stream; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return NULL; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/* ------------------------------------------------------------------------ 17562306a36Sopenharmony_ci * Streaming Object Management 17662306a36Sopenharmony_ci */ 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void uvc_stream_delete(struct uvc_streaming *stream) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci if (stream->async_wq) 18162306a36Sopenharmony_ci destroy_workqueue(stream->async_wq); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci mutex_destroy(&stream->mutex); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci usb_put_intf(stream->intf); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci kfree(stream->formats); 18862306a36Sopenharmony_ci kfree(stream->header.bmaControls); 18962306a36Sopenharmony_ci kfree(stream); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic struct uvc_streaming *uvc_stream_new(struct uvc_device *dev, 19362306a36Sopenharmony_ci struct usb_interface *intf) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct uvc_streaming *stream; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci stream = kzalloc(sizeof(*stream), GFP_KERNEL); 19862306a36Sopenharmony_ci if (stream == NULL) 19962306a36Sopenharmony_ci return NULL; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci mutex_init(&stream->mutex); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci stream->dev = dev; 20462306a36Sopenharmony_ci stream->intf = usb_get_intf(intf); 20562306a36Sopenharmony_ci stream->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* Allocate a stream specific work queue for asynchronous tasks. */ 20862306a36Sopenharmony_ci stream->async_wq = alloc_workqueue("uvcvideo", WQ_UNBOUND | WQ_HIGHPRI, 20962306a36Sopenharmony_ci 0); 21062306a36Sopenharmony_ci if (!stream->async_wq) { 21162306a36Sopenharmony_ci uvc_stream_delete(stream); 21262306a36Sopenharmony_ci return NULL; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return stream; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/* ------------------------------------------------------------------------ 21962306a36Sopenharmony_ci * Descriptors parsing 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic int uvc_parse_format(struct uvc_device *dev, 22362306a36Sopenharmony_ci struct uvc_streaming *streaming, struct uvc_format *format, 22462306a36Sopenharmony_ci struct uvc_frame *frames, u32 **intervals, const unsigned char *buffer, 22562306a36Sopenharmony_ci int buflen) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct usb_interface *intf = streaming->intf; 22862306a36Sopenharmony_ci struct usb_host_interface *alts = intf->cur_altsetting; 22962306a36Sopenharmony_ci const struct uvc_format_desc *fmtdesc; 23062306a36Sopenharmony_ci struct uvc_frame *frame; 23162306a36Sopenharmony_ci const unsigned char *start = buffer; 23262306a36Sopenharmony_ci unsigned int width_multiplier = 1; 23362306a36Sopenharmony_ci unsigned int interval; 23462306a36Sopenharmony_ci unsigned int i, n; 23562306a36Sopenharmony_ci u8 ftype; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci format->type = buffer[2]; 23862306a36Sopenharmony_ci format->index = buffer[3]; 23962306a36Sopenharmony_ci format->frames = frames; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci switch (buffer[2]) { 24262306a36Sopenharmony_ci case UVC_VS_FORMAT_UNCOMPRESSED: 24362306a36Sopenharmony_ci case UVC_VS_FORMAT_FRAME_BASED: 24462306a36Sopenharmony_ci n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28; 24562306a36Sopenharmony_ci if (buflen < n) { 24662306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 24762306a36Sopenharmony_ci "device %d videostreaming interface %d FORMAT error\n", 24862306a36Sopenharmony_ci dev->udev->devnum, 24962306a36Sopenharmony_ci alts->desc.bInterfaceNumber); 25062306a36Sopenharmony_ci return -EINVAL; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* Find the format descriptor from its GUID. */ 25462306a36Sopenharmony_ci fmtdesc = uvc_format_by_guid(&buffer[5]); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (!fmtdesc) { 25762306a36Sopenharmony_ci /* 25862306a36Sopenharmony_ci * Unknown video formats are not fatal errors, the 25962306a36Sopenharmony_ci * caller will skip this descriptor. 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ci dev_info(&streaming->intf->dev, 26262306a36Sopenharmony_ci "Unknown video format %pUl\n", &buffer[5]); 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci format->fcc = fmtdesc->fcc; 26762306a36Sopenharmony_ci format->bpp = buffer[21]; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* 27062306a36Sopenharmony_ci * Some devices report a format that doesn't match what they 27162306a36Sopenharmony_ci * really send. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ci if (dev->quirks & UVC_QUIRK_FORCE_Y8) { 27462306a36Sopenharmony_ci if (format->fcc == V4L2_PIX_FMT_YUYV) { 27562306a36Sopenharmony_ci format->fcc = V4L2_PIX_FMT_GREY; 27662306a36Sopenharmony_ci format->bpp = 8; 27762306a36Sopenharmony_ci width_multiplier = 2; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* Some devices report bpp that doesn't match the format. */ 28262306a36Sopenharmony_ci if (dev->quirks & UVC_QUIRK_FORCE_BPP) { 28362306a36Sopenharmony_ci const struct v4l2_format_info *info = 28462306a36Sopenharmony_ci v4l2_format_info(format->fcc); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci if (info) { 28762306a36Sopenharmony_ci unsigned int div = info->hdiv * info->vdiv; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci n = info->bpp[0] * div; 29062306a36Sopenharmony_ci for (i = 1; i < info->comp_planes; i++) 29162306a36Sopenharmony_ci n += info->bpp[i]; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci format->bpp = DIV_ROUND_UP(8 * n, div); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) { 29862306a36Sopenharmony_ci ftype = UVC_VS_FRAME_UNCOMPRESSED; 29962306a36Sopenharmony_ci } else { 30062306a36Sopenharmony_ci ftype = UVC_VS_FRAME_FRAME_BASED; 30162306a36Sopenharmony_ci if (buffer[27]) 30262306a36Sopenharmony_ci format->flags = UVC_FMT_FLAG_COMPRESSED; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci case UVC_VS_FORMAT_MJPEG: 30762306a36Sopenharmony_ci if (buflen < 11) { 30862306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 30962306a36Sopenharmony_ci "device %d videostreaming interface %d FORMAT error\n", 31062306a36Sopenharmony_ci dev->udev->devnum, 31162306a36Sopenharmony_ci alts->desc.bInterfaceNumber); 31262306a36Sopenharmony_ci return -EINVAL; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci format->fcc = V4L2_PIX_FMT_MJPEG; 31662306a36Sopenharmony_ci format->flags = UVC_FMT_FLAG_COMPRESSED; 31762306a36Sopenharmony_ci format->bpp = 0; 31862306a36Sopenharmony_ci ftype = UVC_VS_FRAME_MJPEG; 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci case UVC_VS_FORMAT_DV: 32262306a36Sopenharmony_ci if (buflen < 9) { 32362306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 32462306a36Sopenharmony_ci "device %d videostreaming interface %d FORMAT error\n", 32562306a36Sopenharmony_ci dev->udev->devnum, 32662306a36Sopenharmony_ci alts->desc.bInterfaceNumber); 32762306a36Sopenharmony_ci return -EINVAL; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if ((buffer[8] & 0x7f) > 2) { 33162306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 33262306a36Sopenharmony_ci "device %d videostreaming interface %d: unknown DV format %u\n", 33362306a36Sopenharmony_ci dev->udev->devnum, 33462306a36Sopenharmony_ci alts->desc.bInterfaceNumber, buffer[8]); 33562306a36Sopenharmony_ci return -EINVAL; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci format->fcc = V4L2_PIX_FMT_DV; 33962306a36Sopenharmony_ci format->flags = UVC_FMT_FLAG_COMPRESSED | UVC_FMT_FLAG_STREAM; 34062306a36Sopenharmony_ci format->bpp = 0; 34162306a36Sopenharmony_ci ftype = 0; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* Create a dummy frame descriptor. */ 34462306a36Sopenharmony_ci frame = &frames[0]; 34562306a36Sopenharmony_ci memset(frame, 0, sizeof(*frame)); 34662306a36Sopenharmony_ci frame->bFrameIntervalType = 1; 34762306a36Sopenharmony_ci frame->dwDefaultFrameInterval = 1; 34862306a36Sopenharmony_ci frame->dwFrameInterval = *intervals; 34962306a36Sopenharmony_ci *(*intervals)++ = 1; 35062306a36Sopenharmony_ci format->nframes = 1; 35162306a36Sopenharmony_ci break; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci case UVC_VS_FORMAT_MPEG2TS: 35462306a36Sopenharmony_ci case UVC_VS_FORMAT_STREAM_BASED: 35562306a36Sopenharmony_ci /* Not supported yet. */ 35662306a36Sopenharmony_ci default: 35762306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 35862306a36Sopenharmony_ci "device %d videostreaming interface %d unsupported format %u\n", 35962306a36Sopenharmony_ci dev->udev->devnum, alts->desc.bInterfaceNumber, 36062306a36Sopenharmony_ci buffer[2]); 36162306a36Sopenharmony_ci return -EINVAL; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci uvc_dbg(dev, DESCR, "Found format %p4cc", &format->fcc); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci buflen -= buffer[0]; 36762306a36Sopenharmony_ci buffer += buffer[0]; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* 37062306a36Sopenharmony_ci * Parse the frame descriptors. Only uncompressed, MJPEG and frame 37162306a36Sopenharmony_ci * based formats have frame descriptors. 37262306a36Sopenharmony_ci */ 37362306a36Sopenharmony_ci while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 37462306a36Sopenharmony_ci buffer[2] == ftype) { 37562306a36Sopenharmony_ci unsigned int maxIntervalIndex; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci frame = &frames[format->nframes]; 37862306a36Sopenharmony_ci if (ftype != UVC_VS_FRAME_FRAME_BASED) 37962306a36Sopenharmony_ci n = buflen > 25 ? buffer[25] : 0; 38062306a36Sopenharmony_ci else 38162306a36Sopenharmony_ci n = buflen > 21 ? buffer[21] : 0; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci n = n ? n : 3; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (buflen < 26 + 4*n) { 38662306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 38762306a36Sopenharmony_ci "device %d videostreaming interface %d FRAME error\n", 38862306a36Sopenharmony_ci dev->udev->devnum, 38962306a36Sopenharmony_ci alts->desc.bInterfaceNumber); 39062306a36Sopenharmony_ci return -EINVAL; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci frame->bFrameIndex = buffer[3]; 39462306a36Sopenharmony_ci frame->bmCapabilities = buffer[4]; 39562306a36Sopenharmony_ci frame->wWidth = get_unaligned_le16(&buffer[5]) 39662306a36Sopenharmony_ci * width_multiplier; 39762306a36Sopenharmony_ci frame->wHeight = get_unaligned_le16(&buffer[7]); 39862306a36Sopenharmony_ci frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); 39962306a36Sopenharmony_ci frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); 40062306a36Sopenharmony_ci if (ftype != UVC_VS_FRAME_FRAME_BASED) { 40162306a36Sopenharmony_ci frame->dwMaxVideoFrameBufferSize = 40262306a36Sopenharmony_ci get_unaligned_le32(&buffer[17]); 40362306a36Sopenharmony_ci frame->dwDefaultFrameInterval = 40462306a36Sopenharmony_ci get_unaligned_le32(&buffer[21]); 40562306a36Sopenharmony_ci frame->bFrameIntervalType = buffer[25]; 40662306a36Sopenharmony_ci } else { 40762306a36Sopenharmony_ci frame->dwMaxVideoFrameBufferSize = 0; 40862306a36Sopenharmony_ci frame->dwDefaultFrameInterval = 40962306a36Sopenharmony_ci get_unaligned_le32(&buffer[17]); 41062306a36Sopenharmony_ci frame->bFrameIntervalType = buffer[21]; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* 41462306a36Sopenharmony_ci * Copy the frame intervals. 41562306a36Sopenharmony_ci * 41662306a36Sopenharmony_ci * Some bogus devices report dwMinFrameInterval equal to 41762306a36Sopenharmony_ci * dwMaxFrameInterval and have dwFrameIntervalStep set to 41862306a36Sopenharmony_ci * zero. Setting all null intervals to 1 fixes the problem and 41962306a36Sopenharmony_ci * some other divisions by zero that could happen. 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_ci frame->dwFrameInterval = *intervals; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci for (i = 0; i < n; ++i) { 42462306a36Sopenharmony_ci interval = get_unaligned_le32(&buffer[26+4*i]); 42562306a36Sopenharmony_ci (*intervals)[i] = interval ? interval : 1; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* 42962306a36Sopenharmony_ci * Apply more fixes, quirks and workarounds to handle incorrect 43062306a36Sopenharmony_ci * or broken descriptors. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* 43462306a36Sopenharmony_ci * Several UVC chipsets screw up dwMaxVideoFrameBufferSize 43562306a36Sopenharmony_ci * completely. Observed behaviours range from setting the 43662306a36Sopenharmony_ci * value to 1.1x the actual frame size to hardwiring the 43762306a36Sopenharmony_ci * 16 low bits to 0. This results in a higher than necessary 43862306a36Sopenharmony_ci * memory usage as well as a wrong image size information. For 43962306a36Sopenharmony_ci * uncompressed formats this can be fixed by computing the 44062306a36Sopenharmony_ci * value from the frame size. 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_ci if (!(format->flags & UVC_FMT_FLAG_COMPRESSED)) 44362306a36Sopenharmony_ci frame->dwMaxVideoFrameBufferSize = format->bpp 44462306a36Sopenharmony_ci * frame->wWidth * frame->wHeight / 8; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* 44762306a36Sopenharmony_ci * Clamp the default frame interval to the boundaries. A zero 44862306a36Sopenharmony_ci * bFrameIntervalType value indicates a continuous frame 44962306a36Sopenharmony_ci * interval range, with dwFrameInterval[0] storing the minimum 45062306a36Sopenharmony_ci * value and dwFrameInterval[1] storing the maximum value. 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ci maxIntervalIndex = frame->bFrameIntervalType ? n - 1 : 1; 45362306a36Sopenharmony_ci frame->dwDefaultFrameInterval = 45462306a36Sopenharmony_ci clamp(frame->dwDefaultFrameInterval, 45562306a36Sopenharmony_ci frame->dwFrameInterval[0], 45662306a36Sopenharmony_ci frame->dwFrameInterval[maxIntervalIndex]); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* 45962306a36Sopenharmony_ci * Some devices report frame intervals that are not functional. 46062306a36Sopenharmony_ci * If the corresponding quirk is set, restrict operation to the 46162306a36Sopenharmony_ci * first interval only. 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_ci if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) { 46462306a36Sopenharmony_ci frame->bFrameIntervalType = 1; 46562306a36Sopenharmony_ci (*intervals)[0] = frame->dwDefaultFrameInterval; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci uvc_dbg(dev, DESCR, "- %ux%u (%u.%u fps)\n", 46962306a36Sopenharmony_ci frame->wWidth, frame->wHeight, 47062306a36Sopenharmony_ci 10000000 / frame->dwDefaultFrameInterval, 47162306a36Sopenharmony_ci (100000000 / frame->dwDefaultFrameInterval) % 10); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci format->nframes++; 47462306a36Sopenharmony_ci *intervals += n; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci buflen -= buffer[0]; 47762306a36Sopenharmony_ci buffer += buffer[0]; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 48162306a36Sopenharmony_ci buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { 48262306a36Sopenharmony_ci buflen -= buffer[0]; 48362306a36Sopenharmony_ci buffer += buffer[0]; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && 48762306a36Sopenharmony_ci buffer[2] == UVC_VS_COLORFORMAT) { 48862306a36Sopenharmony_ci if (buflen < 6) { 48962306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 49062306a36Sopenharmony_ci "device %d videostreaming interface %d COLORFORMAT error\n", 49162306a36Sopenharmony_ci dev->udev->devnum, 49262306a36Sopenharmony_ci alts->desc.bInterfaceNumber); 49362306a36Sopenharmony_ci return -EINVAL; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci format->colorspace = uvc_colorspace(buffer[3]); 49762306a36Sopenharmony_ci format->xfer_func = uvc_xfer_func(buffer[4]); 49862306a36Sopenharmony_ci format->ycbcr_enc = uvc_ycbcr_enc(buffer[5]); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci buflen -= buffer[0]; 50162306a36Sopenharmony_ci buffer += buffer[0]; 50262306a36Sopenharmony_ci } else { 50362306a36Sopenharmony_ci format->colorspace = V4L2_COLORSPACE_SRGB; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci return buffer - start; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int uvc_parse_streaming(struct uvc_device *dev, 51062306a36Sopenharmony_ci struct usb_interface *intf) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct uvc_streaming *streaming = NULL; 51362306a36Sopenharmony_ci struct uvc_format *format; 51462306a36Sopenharmony_ci struct uvc_frame *frame; 51562306a36Sopenharmony_ci struct usb_host_interface *alts = &intf->altsetting[0]; 51662306a36Sopenharmony_ci const unsigned char *_buffer, *buffer = alts->extra; 51762306a36Sopenharmony_ci int _buflen, buflen = alts->extralen; 51862306a36Sopenharmony_ci unsigned int nformats = 0, nframes = 0, nintervals = 0; 51962306a36Sopenharmony_ci unsigned int size, i, n, p; 52062306a36Sopenharmony_ci u32 *interval; 52162306a36Sopenharmony_ci u16 psize; 52262306a36Sopenharmony_ci int ret = -EINVAL; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (intf->cur_altsetting->desc.bInterfaceSubClass 52562306a36Sopenharmony_ci != UVC_SC_VIDEOSTREAMING) { 52662306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 52762306a36Sopenharmony_ci "device %d interface %d isn't a video streaming interface\n", 52862306a36Sopenharmony_ci dev->udev->devnum, 52962306a36Sopenharmony_ci intf->altsetting[0].desc.bInterfaceNumber); 53062306a36Sopenharmony_ci return -EINVAL; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (usb_driver_claim_interface(&uvc_driver.driver, intf, dev)) { 53462306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 53562306a36Sopenharmony_ci "device %d interface %d is already claimed\n", 53662306a36Sopenharmony_ci dev->udev->devnum, 53762306a36Sopenharmony_ci intf->altsetting[0].desc.bInterfaceNumber); 53862306a36Sopenharmony_ci return -EINVAL; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci streaming = uvc_stream_new(dev, intf); 54262306a36Sopenharmony_ci if (streaming == NULL) { 54362306a36Sopenharmony_ci usb_driver_release_interface(&uvc_driver.driver, intf); 54462306a36Sopenharmony_ci return -ENOMEM; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* 54862306a36Sopenharmony_ci * The Pico iMage webcam has its class-specific interface descriptors 54962306a36Sopenharmony_ci * after the endpoint descriptors. 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_ci if (buflen == 0) { 55262306a36Sopenharmony_ci for (i = 0; i < alts->desc.bNumEndpoints; ++i) { 55362306a36Sopenharmony_ci struct usb_host_endpoint *ep = &alts->endpoint[i]; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (ep->extralen == 0) 55662306a36Sopenharmony_ci continue; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (ep->extralen > 2 && 55962306a36Sopenharmony_ci ep->extra[1] == USB_DT_CS_INTERFACE) { 56062306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 56162306a36Sopenharmony_ci "trying extra data from endpoint %u\n", 56262306a36Sopenharmony_ci i); 56362306a36Sopenharmony_ci buffer = alts->endpoint[i].extra; 56462306a36Sopenharmony_ci buflen = alts->endpoint[i].extralen; 56562306a36Sopenharmony_ci break; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* Skip the standard interface descriptors. */ 57162306a36Sopenharmony_ci while (buflen > 2 && buffer[1] != USB_DT_CS_INTERFACE) { 57262306a36Sopenharmony_ci buflen -= buffer[0]; 57362306a36Sopenharmony_ci buffer += buffer[0]; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (buflen <= 2) { 57762306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 57862306a36Sopenharmony_ci "no class-specific streaming interface descriptors found\n"); 57962306a36Sopenharmony_ci goto error; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* Parse the header descriptor. */ 58362306a36Sopenharmony_ci switch (buffer[2]) { 58462306a36Sopenharmony_ci case UVC_VS_OUTPUT_HEADER: 58562306a36Sopenharmony_ci streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 58662306a36Sopenharmony_ci size = 9; 58762306a36Sopenharmony_ci break; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci case UVC_VS_INPUT_HEADER: 59062306a36Sopenharmony_ci streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 59162306a36Sopenharmony_ci size = 13; 59262306a36Sopenharmony_ci break; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci default: 59562306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 59662306a36Sopenharmony_ci "device %d videostreaming interface %d HEADER descriptor not found\n", 59762306a36Sopenharmony_ci dev->udev->devnum, alts->desc.bInterfaceNumber); 59862306a36Sopenharmony_ci goto error; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci p = buflen >= 4 ? buffer[3] : 0; 60262306a36Sopenharmony_ci n = buflen >= size ? buffer[size-1] : 0; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (buflen < size + p*n) { 60562306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 60662306a36Sopenharmony_ci "device %d videostreaming interface %d HEADER descriptor is invalid\n", 60762306a36Sopenharmony_ci dev->udev->devnum, alts->desc.bInterfaceNumber); 60862306a36Sopenharmony_ci goto error; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci streaming->header.bNumFormats = p; 61262306a36Sopenharmony_ci streaming->header.bEndpointAddress = buffer[6]; 61362306a36Sopenharmony_ci if (buffer[2] == UVC_VS_INPUT_HEADER) { 61462306a36Sopenharmony_ci streaming->header.bmInfo = buffer[7]; 61562306a36Sopenharmony_ci streaming->header.bTerminalLink = buffer[8]; 61662306a36Sopenharmony_ci streaming->header.bStillCaptureMethod = buffer[9]; 61762306a36Sopenharmony_ci streaming->header.bTriggerSupport = buffer[10]; 61862306a36Sopenharmony_ci streaming->header.bTriggerUsage = buffer[11]; 61962306a36Sopenharmony_ci } else { 62062306a36Sopenharmony_ci streaming->header.bTerminalLink = buffer[7]; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci streaming->header.bControlSize = n; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci streaming->header.bmaControls = kmemdup(&buffer[size], p * n, 62562306a36Sopenharmony_ci GFP_KERNEL); 62662306a36Sopenharmony_ci if (streaming->header.bmaControls == NULL) { 62762306a36Sopenharmony_ci ret = -ENOMEM; 62862306a36Sopenharmony_ci goto error; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci buflen -= buffer[0]; 63262306a36Sopenharmony_ci buffer += buffer[0]; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci _buffer = buffer; 63562306a36Sopenharmony_ci _buflen = buflen; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* Count the format and frame descriptors. */ 63862306a36Sopenharmony_ci while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) { 63962306a36Sopenharmony_ci switch (_buffer[2]) { 64062306a36Sopenharmony_ci case UVC_VS_FORMAT_UNCOMPRESSED: 64162306a36Sopenharmony_ci case UVC_VS_FORMAT_MJPEG: 64262306a36Sopenharmony_ci case UVC_VS_FORMAT_FRAME_BASED: 64362306a36Sopenharmony_ci nformats++; 64462306a36Sopenharmony_ci break; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci case UVC_VS_FORMAT_DV: 64762306a36Sopenharmony_ci /* 64862306a36Sopenharmony_ci * DV format has no frame descriptor. We will create a 64962306a36Sopenharmony_ci * dummy frame descriptor with a dummy frame interval. 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci nformats++; 65262306a36Sopenharmony_ci nframes++; 65362306a36Sopenharmony_ci nintervals++; 65462306a36Sopenharmony_ci break; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci case UVC_VS_FORMAT_MPEG2TS: 65762306a36Sopenharmony_ci case UVC_VS_FORMAT_STREAM_BASED: 65862306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 65962306a36Sopenharmony_ci "device %d videostreaming interface %d FORMAT %u is not supported\n", 66062306a36Sopenharmony_ci dev->udev->devnum, 66162306a36Sopenharmony_ci alts->desc.bInterfaceNumber, _buffer[2]); 66262306a36Sopenharmony_ci break; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci case UVC_VS_FRAME_UNCOMPRESSED: 66562306a36Sopenharmony_ci case UVC_VS_FRAME_MJPEG: 66662306a36Sopenharmony_ci nframes++; 66762306a36Sopenharmony_ci if (_buflen > 25) 66862306a36Sopenharmony_ci nintervals += _buffer[25] ? _buffer[25] : 3; 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci case UVC_VS_FRAME_FRAME_BASED: 67262306a36Sopenharmony_ci nframes++; 67362306a36Sopenharmony_ci if (_buflen > 21) 67462306a36Sopenharmony_ci nintervals += _buffer[21] ? _buffer[21] : 3; 67562306a36Sopenharmony_ci break; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci _buflen -= _buffer[0]; 67962306a36Sopenharmony_ci _buffer += _buffer[0]; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (nformats == 0) { 68362306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 68462306a36Sopenharmony_ci "device %d videostreaming interface %d has no supported formats defined\n", 68562306a36Sopenharmony_ci dev->udev->devnum, alts->desc.bInterfaceNumber); 68662306a36Sopenharmony_ci goto error; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci size = nformats * sizeof(*format) + nframes * sizeof(*frame) 69062306a36Sopenharmony_ci + nintervals * sizeof(*interval); 69162306a36Sopenharmony_ci format = kzalloc(size, GFP_KERNEL); 69262306a36Sopenharmony_ci if (format == NULL) { 69362306a36Sopenharmony_ci ret = -ENOMEM; 69462306a36Sopenharmony_ci goto error; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci frame = (struct uvc_frame *)&format[nformats]; 69862306a36Sopenharmony_ci interval = (u32 *)&frame[nframes]; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci streaming->formats = format; 70162306a36Sopenharmony_ci streaming->nformats = 0; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci /* Parse the format descriptors. */ 70462306a36Sopenharmony_ci while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) { 70562306a36Sopenharmony_ci switch (buffer[2]) { 70662306a36Sopenharmony_ci case UVC_VS_FORMAT_UNCOMPRESSED: 70762306a36Sopenharmony_ci case UVC_VS_FORMAT_MJPEG: 70862306a36Sopenharmony_ci case UVC_VS_FORMAT_DV: 70962306a36Sopenharmony_ci case UVC_VS_FORMAT_FRAME_BASED: 71062306a36Sopenharmony_ci ret = uvc_parse_format(dev, streaming, format, frame, 71162306a36Sopenharmony_ci &interval, buffer, buflen); 71262306a36Sopenharmony_ci if (ret < 0) 71362306a36Sopenharmony_ci goto error; 71462306a36Sopenharmony_ci if (!ret) 71562306a36Sopenharmony_ci break; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci streaming->nformats++; 71862306a36Sopenharmony_ci frame += format->nframes; 71962306a36Sopenharmony_ci format++; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci buflen -= ret; 72262306a36Sopenharmony_ci buffer += ret; 72362306a36Sopenharmony_ci continue; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci default: 72662306a36Sopenharmony_ci break; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci buflen -= buffer[0]; 73062306a36Sopenharmony_ci buffer += buffer[0]; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if (buflen) 73462306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 73562306a36Sopenharmony_ci "device %d videostreaming interface %d has %u bytes of trailing descriptor garbage\n", 73662306a36Sopenharmony_ci dev->udev->devnum, alts->desc.bInterfaceNumber, buflen); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* Parse the alternate settings to find the maximum bandwidth. */ 73962306a36Sopenharmony_ci for (i = 0; i < intf->num_altsetting; ++i) { 74062306a36Sopenharmony_ci struct usb_host_endpoint *ep; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci alts = &intf->altsetting[i]; 74362306a36Sopenharmony_ci ep = uvc_find_endpoint(alts, 74462306a36Sopenharmony_ci streaming->header.bEndpointAddress); 74562306a36Sopenharmony_ci if (ep == NULL) 74662306a36Sopenharmony_ci continue; 74762306a36Sopenharmony_ci psize = uvc_endpoint_max_bpi(dev->udev, ep); 74862306a36Sopenharmony_ci if (psize > streaming->maxpsize) 74962306a36Sopenharmony_ci streaming->maxpsize = psize; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci list_add_tail(&streaming->list, &dev->streams); 75362306a36Sopenharmony_ci return 0; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cierror: 75662306a36Sopenharmony_ci usb_driver_release_interface(&uvc_driver.driver, intf); 75762306a36Sopenharmony_ci uvc_stream_delete(streaming); 75862306a36Sopenharmony_ci return ret; 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic const u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA; 76262306a36Sopenharmony_cistatic const u8 uvc_gpio_guid[16] = UVC_GUID_EXT_GPIO_CONTROLLER; 76362306a36Sopenharmony_cistatic const u8 uvc_media_transport_input_guid[16] = 76462306a36Sopenharmony_ci UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT; 76562306a36Sopenharmony_cistatic const u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_cistatic struct uvc_entity *uvc_alloc_entity(u16 type, u16 id, 76862306a36Sopenharmony_ci unsigned int num_pads, unsigned int extra_size) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci struct uvc_entity *entity; 77162306a36Sopenharmony_ci unsigned int num_inputs; 77262306a36Sopenharmony_ci unsigned int size; 77362306a36Sopenharmony_ci unsigned int i; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci extra_size = roundup(extra_size, sizeof(*entity->pads)); 77662306a36Sopenharmony_ci if (num_pads) 77762306a36Sopenharmony_ci num_inputs = type & UVC_TERM_OUTPUT ? num_pads : num_pads - 1; 77862306a36Sopenharmony_ci else 77962306a36Sopenharmony_ci num_inputs = 0; 78062306a36Sopenharmony_ci size = sizeof(*entity) + extra_size + sizeof(*entity->pads) * num_pads 78162306a36Sopenharmony_ci + num_inputs; 78262306a36Sopenharmony_ci entity = kzalloc(size, GFP_KERNEL); 78362306a36Sopenharmony_ci if (entity == NULL) 78462306a36Sopenharmony_ci return NULL; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci entity->id = id; 78762306a36Sopenharmony_ci entity->type = type; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* 79062306a36Sopenharmony_ci * Set the GUID for standard entity types. For extension units, the GUID 79162306a36Sopenharmony_ci * is initialized by the caller. 79262306a36Sopenharmony_ci */ 79362306a36Sopenharmony_ci switch (type) { 79462306a36Sopenharmony_ci case UVC_EXT_GPIO_UNIT: 79562306a36Sopenharmony_ci memcpy(entity->guid, uvc_gpio_guid, 16); 79662306a36Sopenharmony_ci break; 79762306a36Sopenharmony_ci case UVC_ITT_CAMERA: 79862306a36Sopenharmony_ci memcpy(entity->guid, uvc_camera_guid, 16); 79962306a36Sopenharmony_ci break; 80062306a36Sopenharmony_ci case UVC_ITT_MEDIA_TRANSPORT_INPUT: 80162306a36Sopenharmony_ci memcpy(entity->guid, uvc_media_transport_input_guid, 16); 80262306a36Sopenharmony_ci break; 80362306a36Sopenharmony_ci case UVC_VC_PROCESSING_UNIT: 80462306a36Sopenharmony_ci memcpy(entity->guid, uvc_processing_guid, 16); 80562306a36Sopenharmony_ci break; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci entity->num_links = 0; 80962306a36Sopenharmony_ci entity->num_pads = num_pads; 81062306a36Sopenharmony_ci entity->pads = ((void *)(entity + 1)) + extra_size; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci for (i = 0; i < num_inputs; ++i) 81362306a36Sopenharmony_ci entity->pads[i].flags = MEDIA_PAD_FL_SINK; 81462306a36Sopenharmony_ci if (!UVC_ENTITY_IS_OTERM(entity) && num_pads) 81562306a36Sopenharmony_ci entity->pads[num_pads-1].flags = MEDIA_PAD_FL_SOURCE; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci entity->bNrInPins = num_inputs; 81862306a36Sopenharmony_ci entity->baSourceID = (u8 *)(&entity->pads[num_pads]); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci return entity; 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic void uvc_entity_set_name(struct uvc_device *dev, struct uvc_entity *entity, 82462306a36Sopenharmony_ci const char *type_name, u8 string_id) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci int ret; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci /* 82962306a36Sopenharmony_ci * First attempt to read the entity name from the device. If the entity 83062306a36Sopenharmony_ci * has no associated string, or if reading the string fails (most 83162306a36Sopenharmony_ci * likely due to a buggy firmware), fall back to default names based on 83262306a36Sopenharmony_ci * the entity type. 83362306a36Sopenharmony_ci */ 83462306a36Sopenharmony_ci if (string_id) { 83562306a36Sopenharmony_ci ret = usb_string(dev->udev, string_id, entity->name, 83662306a36Sopenharmony_ci sizeof(entity->name)); 83762306a36Sopenharmony_ci if (!ret) 83862306a36Sopenharmony_ci return; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci sprintf(entity->name, "%s %u", type_name, entity->id); 84262306a36Sopenharmony_ci} 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci/* Parse vendor-specific extensions. */ 84562306a36Sopenharmony_cistatic int uvc_parse_vendor_control(struct uvc_device *dev, 84662306a36Sopenharmony_ci const unsigned char *buffer, int buflen) 84762306a36Sopenharmony_ci{ 84862306a36Sopenharmony_ci struct usb_device *udev = dev->udev; 84962306a36Sopenharmony_ci struct usb_host_interface *alts = dev->intf->cur_altsetting; 85062306a36Sopenharmony_ci struct uvc_entity *unit; 85162306a36Sopenharmony_ci unsigned int n, p; 85262306a36Sopenharmony_ci int handled = 0; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci switch (le16_to_cpu(dev->udev->descriptor.idVendor)) { 85562306a36Sopenharmony_ci case 0x046d: /* Logitech */ 85662306a36Sopenharmony_ci if (buffer[1] != 0x41 || buffer[2] != 0x01) 85762306a36Sopenharmony_ci break; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* 86062306a36Sopenharmony_ci * Logitech implements several vendor specific functions 86162306a36Sopenharmony_ci * through vendor specific extension units (LXU). 86262306a36Sopenharmony_ci * 86362306a36Sopenharmony_ci * The LXU descriptors are similar to XU descriptors 86462306a36Sopenharmony_ci * (see "USB Device Video Class for Video Devices", section 86562306a36Sopenharmony_ci * 3.7.2.6 "Extension Unit Descriptor") with the following 86662306a36Sopenharmony_ci * differences: 86762306a36Sopenharmony_ci * 86862306a36Sopenharmony_ci * ---------------------------------------------------------- 86962306a36Sopenharmony_ci * 0 bLength 1 Number 87062306a36Sopenharmony_ci * Size of this descriptor, in bytes: 24+p+n*2 87162306a36Sopenharmony_ci * ---------------------------------------------------------- 87262306a36Sopenharmony_ci * 23+p+n bmControlsType N Bitmap 87362306a36Sopenharmony_ci * Individual bits in the set are defined: 87462306a36Sopenharmony_ci * 0: Absolute 87562306a36Sopenharmony_ci * 1: Relative 87662306a36Sopenharmony_ci * 87762306a36Sopenharmony_ci * This bitset is mapped exactly the same as bmControls. 87862306a36Sopenharmony_ci * ---------------------------------------------------------- 87962306a36Sopenharmony_ci * 23+p+n*2 bReserved 1 Boolean 88062306a36Sopenharmony_ci * ---------------------------------------------------------- 88162306a36Sopenharmony_ci * 24+p+n*2 iExtension 1 Index 88262306a36Sopenharmony_ci * Index of a string descriptor that describes this 88362306a36Sopenharmony_ci * extension unit. 88462306a36Sopenharmony_ci * ---------------------------------------------------------- 88562306a36Sopenharmony_ci */ 88662306a36Sopenharmony_ci p = buflen >= 22 ? buffer[21] : 0; 88762306a36Sopenharmony_ci n = buflen >= 25 + p ? buffer[22+p] : 0; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci if (buflen < 25 + p + 2*n) { 89062306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 89162306a36Sopenharmony_ci "device %d videocontrol interface %d EXTENSION_UNIT error\n", 89262306a36Sopenharmony_ci udev->devnum, alts->desc.bInterfaceNumber); 89362306a36Sopenharmony_ci break; 89462306a36Sopenharmony_ci } 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3], 89762306a36Sopenharmony_ci p + 1, 2*n); 89862306a36Sopenharmony_ci if (unit == NULL) 89962306a36Sopenharmony_ci return -ENOMEM; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci memcpy(unit->guid, &buffer[4], 16); 90262306a36Sopenharmony_ci unit->extension.bNumControls = buffer[20]; 90362306a36Sopenharmony_ci memcpy(unit->baSourceID, &buffer[22], p); 90462306a36Sopenharmony_ci unit->extension.bControlSize = buffer[22+p]; 90562306a36Sopenharmony_ci unit->extension.bmControls = (u8 *)unit + sizeof(*unit); 90662306a36Sopenharmony_ci unit->extension.bmControlsType = (u8 *)unit + sizeof(*unit) 90762306a36Sopenharmony_ci + n; 90862306a36Sopenharmony_ci memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci uvc_entity_set_name(dev, unit, "Extension", buffer[24+p+2*n]); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci list_add_tail(&unit->list, &dev->entities); 91362306a36Sopenharmony_ci handled = 1; 91462306a36Sopenharmony_ci break; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci return handled; 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_cistatic int uvc_parse_standard_control(struct uvc_device *dev, 92162306a36Sopenharmony_ci const unsigned char *buffer, int buflen) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci struct usb_device *udev = dev->udev; 92462306a36Sopenharmony_ci struct uvc_entity *unit, *term; 92562306a36Sopenharmony_ci struct usb_interface *intf; 92662306a36Sopenharmony_ci struct usb_host_interface *alts = dev->intf->cur_altsetting; 92762306a36Sopenharmony_ci unsigned int i, n, p, len; 92862306a36Sopenharmony_ci const char *type_name; 92962306a36Sopenharmony_ci u16 type; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci switch (buffer[2]) { 93262306a36Sopenharmony_ci case UVC_VC_HEADER: 93362306a36Sopenharmony_ci n = buflen >= 12 ? buffer[11] : 0; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci if (buflen < 12 + n) { 93662306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 93762306a36Sopenharmony_ci "device %d videocontrol interface %d HEADER error\n", 93862306a36Sopenharmony_ci udev->devnum, alts->desc.bInterfaceNumber); 93962306a36Sopenharmony_ci return -EINVAL; 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci dev->uvc_version = get_unaligned_le16(&buffer[3]); 94362306a36Sopenharmony_ci dev->clock_frequency = get_unaligned_le32(&buffer[7]); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci /* Parse all USB Video Streaming interfaces. */ 94662306a36Sopenharmony_ci for (i = 0; i < n; ++i) { 94762306a36Sopenharmony_ci intf = usb_ifnum_to_if(udev, buffer[12+i]); 94862306a36Sopenharmony_ci if (intf == NULL) { 94962306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 95062306a36Sopenharmony_ci "device %d interface %d doesn't exists\n", 95162306a36Sopenharmony_ci udev->devnum, i); 95262306a36Sopenharmony_ci continue; 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci uvc_parse_streaming(dev, intf); 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci break; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci case UVC_VC_INPUT_TERMINAL: 96062306a36Sopenharmony_ci if (buflen < 8) { 96162306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 96262306a36Sopenharmony_ci "device %d videocontrol interface %d INPUT_TERMINAL error\n", 96362306a36Sopenharmony_ci udev->devnum, alts->desc.bInterfaceNumber); 96462306a36Sopenharmony_ci return -EINVAL; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci /* 96862306a36Sopenharmony_ci * Reject invalid terminal types that would cause issues: 96962306a36Sopenharmony_ci * 97062306a36Sopenharmony_ci * - The high byte must be non-zero, otherwise it would be 97162306a36Sopenharmony_ci * confused with a unit. 97262306a36Sopenharmony_ci * 97362306a36Sopenharmony_ci * - Bit 15 must be 0, as we use it internally as a terminal 97462306a36Sopenharmony_ci * direction flag. 97562306a36Sopenharmony_ci * 97662306a36Sopenharmony_ci * Other unknown types are accepted. 97762306a36Sopenharmony_ci */ 97862306a36Sopenharmony_ci type = get_unaligned_le16(&buffer[4]); 97962306a36Sopenharmony_ci if ((type & 0x7f00) == 0 || (type & 0x8000) != 0) { 98062306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 98162306a36Sopenharmony_ci "device %d videocontrol interface %d INPUT_TERMINAL %d has invalid type 0x%04x, skipping\n", 98262306a36Sopenharmony_ci udev->devnum, alts->desc.bInterfaceNumber, 98362306a36Sopenharmony_ci buffer[3], type); 98462306a36Sopenharmony_ci return 0; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci n = 0; 98862306a36Sopenharmony_ci p = 0; 98962306a36Sopenharmony_ci len = 8; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci if (type == UVC_ITT_CAMERA) { 99262306a36Sopenharmony_ci n = buflen >= 15 ? buffer[14] : 0; 99362306a36Sopenharmony_ci len = 15; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci } else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) { 99662306a36Sopenharmony_ci n = buflen >= 9 ? buffer[8] : 0; 99762306a36Sopenharmony_ci p = buflen >= 10 + n ? buffer[9+n] : 0; 99862306a36Sopenharmony_ci len = 10; 99962306a36Sopenharmony_ci } 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci if (buflen < len + n + p) { 100262306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 100362306a36Sopenharmony_ci "device %d videocontrol interface %d INPUT_TERMINAL error\n", 100462306a36Sopenharmony_ci udev->devnum, alts->desc.bInterfaceNumber); 100562306a36Sopenharmony_ci return -EINVAL; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3], 100962306a36Sopenharmony_ci 1, n + p); 101062306a36Sopenharmony_ci if (term == NULL) 101162306a36Sopenharmony_ci return -ENOMEM; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) { 101462306a36Sopenharmony_ci term->camera.bControlSize = n; 101562306a36Sopenharmony_ci term->camera.bmControls = (u8 *)term + sizeof(*term); 101662306a36Sopenharmony_ci term->camera.wObjectiveFocalLengthMin = 101762306a36Sopenharmony_ci get_unaligned_le16(&buffer[8]); 101862306a36Sopenharmony_ci term->camera.wObjectiveFocalLengthMax = 101962306a36Sopenharmony_ci get_unaligned_le16(&buffer[10]); 102062306a36Sopenharmony_ci term->camera.wOcularFocalLength = 102162306a36Sopenharmony_ci get_unaligned_le16(&buffer[12]); 102262306a36Sopenharmony_ci memcpy(term->camera.bmControls, &buffer[15], n); 102362306a36Sopenharmony_ci } else if (UVC_ENTITY_TYPE(term) == 102462306a36Sopenharmony_ci UVC_ITT_MEDIA_TRANSPORT_INPUT) { 102562306a36Sopenharmony_ci term->media.bControlSize = n; 102662306a36Sopenharmony_ci term->media.bmControls = (u8 *)term + sizeof(*term); 102762306a36Sopenharmony_ci term->media.bTransportModeSize = p; 102862306a36Sopenharmony_ci term->media.bmTransportModes = (u8 *)term 102962306a36Sopenharmony_ci + sizeof(*term) + n; 103062306a36Sopenharmony_ci memcpy(term->media.bmControls, &buffer[9], n); 103162306a36Sopenharmony_ci memcpy(term->media.bmTransportModes, &buffer[10+n], p); 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) 103562306a36Sopenharmony_ci type_name = "Camera"; 103662306a36Sopenharmony_ci else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT) 103762306a36Sopenharmony_ci type_name = "Media"; 103862306a36Sopenharmony_ci else 103962306a36Sopenharmony_ci type_name = "Input"; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci uvc_entity_set_name(dev, term, type_name, buffer[7]); 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci list_add_tail(&term->list, &dev->entities); 104462306a36Sopenharmony_ci break; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci case UVC_VC_OUTPUT_TERMINAL: 104762306a36Sopenharmony_ci if (buflen < 9) { 104862306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 104962306a36Sopenharmony_ci "device %d videocontrol interface %d OUTPUT_TERMINAL error\n", 105062306a36Sopenharmony_ci udev->devnum, alts->desc.bInterfaceNumber); 105162306a36Sopenharmony_ci return -EINVAL; 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci /* 105562306a36Sopenharmony_ci * Make sure the terminal type MSB is not null, otherwise it 105662306a36Sopenharmony_ci * could be confused with a unit. 105762306a36Sopenharmony_ci */ 105862306a36Sopenharmony_ci type = get_unaligned_le16(&buffer[4]); 105962306a36Sopenharmony_ci if ((type & 0xff00) == 0) { 106062306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 106162306a36Sopenharmony_ci "device %d videocontrol interface %d OUTPUT_TERMINAL %d has invalid type 0x%04x, skipping\n", 106262306a36Sopenharmony_ci udev->devnum, alts->desc.bInterfaceNumber, 106362306a36Sopenharmony_ci buffer[3], type); 106462306a36Sopenharmony_ci return 0; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3], 106862306a36Sopenharmony_ci 1, 0); 106962306a36Sopenharmony_ci if (term == NULL) 107062306a36Sopenharmony_ci return -ENOMEM; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci memcpy(term->baSourceID, &buffer[7], 1); 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci uvc_entity_set_name(dev, term, "Output", buffer[8]); 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci list_add_tail(&term->list, &dev->entities); 107762306a36Sopenharmony_ci break; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci case UVC_VC_SELECTOR_UNIT: 108062306a36Sopenharmony_ci p = buflen >= 5 ? buffer[4] : 0; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci if (buflen < 5 || buflen < 6 + p) { 108362306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 108462306a36Sopenharmony_ci "device %d videocontrol interface %d SELECTOR_UNIT error\n", 108562306a36Sopenharmony_ci udev->devnum, alts->desc.bInterfaceNumber); 108662306a36Sopenharmony_ci return -EINVAL; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0); 109062306a36Sopenharmony_ci if (unit == NULL) 109162306a36Sopenharmony_ci return -ENOMEM; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci memcpy(unit->baSourceID, &buffer[5], p); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci uvc_entity_set_name(dev, unit, "Selector", buffer[5+p]); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci list_add_tail(&unit->list, &dev->entities); 109862306a36Sopenharmony_ci break; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci case UVC_VC_PROCESSING_UNIT: 110162306a36Sopenharmony_ci n = buflen >= 8 ? buffer[7] : 0; 110262306a36Sopenharmony_ci p = dev->uvc_version >= 0x0110 ? 10 : 9; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci if (buflen < p + n) { 110562306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 110662306a36Sopenharmony_ci "device %d videocontrol interface %d PROCESSING_UNIT error\n", 110762306a36Sopenharmony_ci udev->devnum, alts->desc.bInterfaceNumber); 110862306a36Sopenharmony_ci return -EINVAL; 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n); 111262306a36Sopenharmony_ci if (unit == NULL) 111362306a36Sopenharmony_ci return -ENOMEM; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci memcpy(unit->baSourceID, &buffer[4], 1); 111662306a36Sopenharmony_ci unit->processing.wMaxMultiplier = 111762306a36Sopenharmony_ci get_unaligned_le16(&buffer[5]); 111862306a36Sopenharmony_ci unit->processing.bControlSize = buffer[7]; 111962306a36Sopenharmony_ci unit->processing.bmControls = (u8 *)unit + sizeof(*unit); 112062306a36Sopenharmony_ci memcpy(unit->processing.bmControls, &buffer[8], n); 112162306a36Sopenharmony_ci if (dev->uvc_version >= 0x0110) 112262306a36Sopenharmony_ci unit->processing.bmVideoStandards = buffer[9+n]; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci uvc_entity_set_name(dev, unit, "Processing", buffer[8+n]); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci list_add_tail(&unit->list, &dev->entities); 112762306a36Sopenharmony_ci break; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci case UVC_VC_EXTENSION_UNIT: 113062306a36Sopenharmony_ci p = buflen >= 22 ? buffer[21] : 0; 113162306a36Sopenharmony_ci n = buflen >= 24 + p ? buffer[22+p] : 0; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci if (buflen < 24 + p + n) { 113462306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 113562306a36Sopenharmony_ci "device %d videocontrol interface %d EXTENSION_UNIT error\n", 113662306a36Sopenharmony_ci udev->devnum, alts->desc.bInterfaceNumber); 113762306a36Sopenharmony_ci return -EINVAL; 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n); 114162306a36Sopenharmony_ci if (unit == NULL) 114262306a36Sopenharmony_ci return -ENOMEM; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci memcpy(unit->guid, &buffer[4], 16); 114562306a36Sopenharmony_ci unit->extension.bNumControls = buffer[20]; 114662306a36Sopenharmony_ci memcpy(unit->baSourceID, &buffer[22], p); 114762306a36Sopenharmony_ci unit->extension.bControlSize = buffer[22+p]; 114862306a36Sopenharmony_ci unit->extension.bmControls = (u8 *)unit + sizeof(*unit); 114962306a36Sopenharmony_ci memcpy(unit->extension.bmControls, &buffer[23+p], n); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci uvc_entity_set_name(dev, unit, "Extension", buffer[23+p+n]); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci list_add_tail(&unit->list, &dev->entities); 115462306a36Sopenharmony_ci break; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci default: 115762306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 115862306a36Sopenharmony_ci "Found an unknown CS_INTERFACE descriptor (%u)\n", 115962306a36Sopenharmony_ci buffer[2]); 116062306a36Sopenharmony_ci break; 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci return 0; 116462306a36Sopenharmony_ci} 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_cistatic int uvc_parse_control(struct uvc_device *dev) 116762306a36Sopenharmony_ci{ 116862306a36Sopenharmony_ci struct usb_host_interface *alts = dev->intf->cur_altsetting; 116962306a36Sopenharmony_ci const unsigned char *buffer = alts->extra; 117062306a36Sopenharmony_ci int buflen = alts->extralen; 117162306a36Sopenharmony_ci int ret; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci /* 117462306a36Sopenharmony_ci * Parse the default alternate setting only, as the UVC specification 117562306a36Sopenharmony_ci * defines a single alternate setting, the default alternate setting 117662306a36Sopenharmony_ci * zero. 117762306a36Sopenharmony_ci */ 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci while (buflen > 2) { 118062306a36Sopenharmony_ci if (uvc_parse_vendor_control(dev, buffer, buflen) || 118162306a36Sopenharmony_ci buffer[1] != USB_DT_CS_INTERFACE) 118262306a36Sopenharmony_ci goto next_descriptor; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci ret = uvc_parse_standard_control(dev, buffer, buflen); 118562306a36Sopenharmony_ci if (ret < 0) 118662306a36Sopenharmony_ci return ret; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_cinext_descriptor: 118962306a36Sopenharmony_ci buflen -= buffer[0]; 119062306a36Sopenharmony_ci buffer += buffer[0]; 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci /* 119462306a36Sopenharmony_ci * Check if the optional status endpoint is present. Built-in iSight 119562306a36Sopenharmony_ci * webcams have an interrupt endpoint but spit proprietary data that 119662306a36Sopenharmony_ci * don't conform to the UVC status endpoint messages. Don't try to 119762306a36Sopenharmony_ci * handle the interrupt endpoint for those cameras. 119862306a36Sopenharmony_ci */ 119962306a36Sopenharmony_ci if (alts->desc.bNumEndpoints == 1 && 120062306a36Sopenharmony_ci !(dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)) { 120162306a36Sopenharmony_ci struct usb_host_endpoint *ep = &alts->endpoint[0]; 120262306a36Sopenharmony_ci struct usb_endpoint_descriptor *desc = &ep->desc; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci if (usb_endpoint_is_int_in(desc) && 120562306a36Sopenharmony_ci le16_to_cpu(desc->wMaxPacketSize) >= 8 && 120662306a36Sopenharmony_ci desc->bInterval != 0) { 120762306a36Sopenharmony_ci uvc_dbg(dev, DESCR, 120862306a36Sopenharmony_ci "Found a Status endpoint (addr %02x)\n", 120962306a36Sopenharmony_ci desc->bEndpointAddress); 121062306a36Sopenharmony_ci dev->int_ep = ep; 121162306a36Sopenharmony_ci } 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci return 0; 121562306a36Sopenharmony_ci} 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 121862306a36Sopenharmony_ci * Privacy GPIO 121962306a36Sopenharmony_ci */ 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_cistatic void uvc_gpio_event(struct uvc_device *dev) 122262306a36Sopenharmony_ci{ 122362306a36Sopenharmony_ci struct uvc_entity *unit = dev->gpio_unit; 122462306a36Sopenharmony_ci struct uvc_video_chain *chain; 122562306a36Sopenharmony_ci u8 new_val; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci if (!unit) 122862306a36Sopenharmony_ci return; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci new_val = gpiod_get_value_cansleep(unit->gpio.gpio_privacy); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci /* GPIO entities are always on the first chain. */ 123362306a36Sopenharmony_ci chain = list_first_entry(&dev->chains, struct uvc_video_chain, list); 123462306a36Sopenharmony_ci uvc_ctrl_status_event(chain, unit->controls, &new_val); 123562306a36Sopenharmony_ci} 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_cistatic int uvc_gpio_get_cur(struct uvc_device *dev, struct uvc_entity *entity, 123862306a36Sopenharmony_ci u8 cs, void *data, u16 size) 123962306a36Sopenharmony_ci{ 124062306a36Sopenharmony_ci if (cs != UVC_CT_PRIVACY_CONTROL || size < 1) 124162306a36Sopenharmony_ci return -EINVAL; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci *(u8 *)data = gpiod_get_value_cansleep(entity->gpio.gpio_privacy); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci return 0; 124662306a36Sopenharmony_ci} 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_cistatic int uvc_gpio_get_info(struct uvc_device *dev, struct uvc_entity *entity, 124962306a36Sopenharmony_ci u8 cs, u8 *caps) 125062306a36Sopenharmony_ci{ 125162306a36Sopenharmony_ci if (cs != UVC_CT_PRIVACY_CONTROL) 125262306a36Sopenharmony_ci return -EINVAL; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci *caps = UVC_CONTROL_CAP_GET | UVC_CONTROL_CAP_AUTOUPDATE; 125562306a36Sopenharmony_ci return 0; 125662306a36Sopenharmony_ci} 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_cistatic irqreturn_t uvc_gpio_irq(int irq, void *data) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci struct uvc_device *dev = data; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci uvc_gpio_event(dev); 126362306a36Sopenharmony_ci return IRQ_HANDLED; 126462306a36Sopenharmony_ci} 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_cistatic int uvc_gpio_parse(struct uvc_device *dev) 126762306a36Sopenharmony_ci{ 126862306a36Sopenharmony_ci struct uvc_entity *unit; 126962306a36Sopenharmony_ci struct gpio_desc *gpio_privacy; 127062306a36Sopenharmony_ci int irq; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci gpio_privacy = devm_gpiod_get_optional(&dev->udev->dev, "privacy", 127362306a36Sopenharmony_ci GPIOD_IN); 127462306a36Sopenharmony_ci if (IS_ERR_OR_NULL(gpio_privacy)) 127562306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(gpio_privacy); 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci irq = gpiod_to_irq(gpio_privacy); 127862306a36Sopenharmony_ci if (irq < 0) 127962306a36Sopenharmony_ci return dev_err_probe(&dev->udev->dev, irq, 128062306a36Sopenharmony_ci "No IRQ for privacy GPIO\n"); 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci unit = uvc_alloc_entity(UVC_EXT_GPIO_UNIT, UVC_EXT_GPIO_UNIT_ID, 0, 1); 128362306a36Sopenharmony_ci if (!unit) 128462306a36Sopenharmony_ci return -ENOMEM; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci unit->gpio.gpio_privacy = gpio_privacy; 128762306a36Sopenharmony_ci unit->gpio.irq = irq; 128862306a36Sopenharmony_ci unit->gpio.bControlSize = 1; 128962306a36Sopenharmony_ci unit->gpio.bmControls = (u8 *)unit + sizeof(*unit); 129062306a36Sopenharmony_ci unit->gpio.bmControls[0] = 1; 129162306a36Sopenharmony_ci unit->get_cur = uvc_gpio_get_cur; 129262306a36Sopenharmony_ci unit->get_info = uvc_gpio_get_info; 129362306a36Sopenharmony_ci strscpy(unit->name, "GPIO", sizeof(unit->name)); 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci list_add_tail(&unit->list, &dev->entities); 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci dev->gpio_unit = unit; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci return 0; 130062306a36Sopenharmony_ci} 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_cistatic int uvc_gpio_init_irq(struct uvc_device *dev) 130362306a36Sopenharmony_ci{ 130462306a36Sopenharmony_ci struct uvc_entity *unit = dev->gpio_unit; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci if (!unit || unit->gpio.irq < 0) 130762306a36Sopenharmony_ci return 0; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci return devm_request_threaded_irq(&dev->udev->dev, unit->gpio.irq, NULL, 131062306a36Sopenharmony_ci uvc_gpio_irq, 131162306a36Sopenharmony_ci IRQF_ONESHOT | IRQF_TRIGGER_FALLING | 131262306a36Sopenharmony_ci IRQF_TRIGGER_RISING, 131362306a36Sopenharmony_ci "uvc_privacy_gpio", dev); 131462306a36Sopenharmony_ci} 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci/* ------------------------------------------------------------------------ 131762306a36Sopenharmony_ci * UVC device scan 131862306a36Sopenharmony_ci */ 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci/* 132162306a36Sopenharmony_ci * Scan the UVC descriptors to locate a chain starting at an Output Terminal 132262306a36Sopenharmony_ci * and containing the following units: 132362306a36Sopenharmony_ci * 132462306a36Sopenharmony_ci * - one or more Output Terminals (USB Streaming or Display) 132562306a36Sopenharmony_ci * - zero or one Processing Unit 132662306a36Sopenharmony_ci * - zero, one or more single-input Selector Units 132762306a36Sopenharmony_ci * - zero or one multiple-input Selector Units, provided all inputs are 132862306a36Sopenharmony_ci * connected to input terminals 132962306a36Sopenharmony_ci * - zero, one or mode single-input Extension Units 133062306a36Sopenharmony_ci * - one or more Input Terminals (Camera, External or USB Streaming) 133162306a36Sopenharmony_ci * 133262306a36Sopenharmony_ci * The terminal and units must match on of the following structures: 133362306a36Sopenharmony_ci * 133462306a36Sopenharmony_ci * ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0) 133562306a36Sopenharmony_ci * ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ... 133662306a36Sopenharmony_ci * ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n) 133762306a36Sopenharmony_ci * 133862306a36Sopenharmony_ci * +---------+ +---------+ -> OTT_*(0) 133962306a36Sopenharmony_ci * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ... 134062306a36Sopenharmony_ci * +---------+ +---------+ -> OTT_*(n) 134162306a36Sopenharmony_ci * 134262306a36Sopenharmony_ci * The Processing Unit and Extension Units can be in any order. Additional 134362306a36Sopenharmony_ci * Extension Units connected to the main chain as single-unit branches are 134462306a36Sopenharmony_ci * also supported. Single-input Selector Units are ignored. 134562306a36Sopenharmony_ci */ 134662306a36Sopenharmony_cistatic int uvc_scan_chain_entity(struct uvc_video_chain *chain, 134762306a36Sopenharmony_ci struct uvc_entity *entity) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci switch (UVC_ENTITY_TYPE(entity)) { 135062306a36Sopenharmony_ci case UVC_VC_EXTENSION_UNIT: 135162306a36Sopenharmony_ci uvc_dbg_cont(PROBE, " <- XU %d", entity->id); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci if (entity->bNrInPins != 1) { 135462306a36Sopenharmony_ci uvc_dbg(chain->dev, DESCR, 135562306a36Sopenharmony_ci "Extension unit %d has more than 1 input pin\n", 135662306a36Sopenharmony_ci entity->id); 135762306a36Sopenharmony_ci return -1; 135862306a36Sopenharmony_ci } 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci break; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci case UVC_VC_PROCESSING_UNIT: 136362306a36Sopenharmony_ci uvc_dbg_cont(PROBE, " <- PU %d", entity->id); 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci if (chain->processing != NULL) { 136662306a36Sopenharmony_ci uvc_dbg(chain->dev, DESCR, 136762306a36Sopenharmony_ci "Found multiple Processing Units in chain\n"); 136862306a36Sopenharmony_ci return -1; 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci chain->processing = entity; 137262306a36Sopenharmony_ci break; 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci case UVC_VC_SELECTOR_UNIT: 137562306a36Sopenharmony_ci uvc_dbg_cont(PROBE, " <- SU %d", entity->id); 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci /* Single-input selector units are ignored. */ 137862306a36Sopenharmony_ci if (entity->bNrInPins == 1) 137962306a36Sopenharmony_ci break; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci if (chain->selector != NULL) { 138262306a36Sopenharmony_ci uvc_dbg(chain->dev, DESCR, 138362306a36Sopenharmony_ci "Found multiple Selector Units in chain\n"); 138462306a36Sopenharmony_ci return -1; 138562306a36Sopenharmony_ci } 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci chain->selector = entity; 138862306a36Sopenharmony_ci break; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci case UVC_ITT_VENDOR_SPECIFIC: 139162306a36Sopenharmony_ci case UVC_ITT_CAMERA: 139262306a36Sopenharmony_ci case UVC_ITT_MEDIA_TRANSPORT_INPUT: 139362306a36Sopenharmony_ci uvc_dbg_cont(PROBE, " <- IT %d\n", entity->id); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci break; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci case UVC_OTT_VENDOR_SPECIFIC: 139862306a36Sopenharmony_ci case UVC_OTT_DISPLAY: 139962306a36Sopenharmony_ci case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 140062306a36Sopenharmony_ci uvc_dbg_cont(PROBE, " OT %d", entity->id); 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci break; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci case UVC_TT_STREAMING: 140562306a36Sopenharmony_ci if (UVC_ENTITY_IS_ITERM(entity)) 140662306a36Sopenharmony_ci uvc_dbg_cont(PROBE, " <- IT %d\n", entity->id); 140762306a36Sopenharmony_ci else 140862306a36Sopenharmony_ci uvc_dbg_cont(PROBE, " OT %d", entity->id); 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci break; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci default: 141362306a36Sopenharmony_ci uvc_dbg(chain->dev, DESCR, 141462306a36Sopenharmony_ci "Unsupported entity type 0x%04x found in chain\n", 141562306a36Sopenharmony_ci UVC_ENTITY_TYPE(entity)); 141662306a36Sopenharmony_ci return -1; 141762306a36Sopenharmony_ci } 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci list_add_tail(&entity->chain, &chain->entities); 142062306a36Sopenharmony_ci return 0; 142162306a36Sopenharmony_ci} 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_cistatic int uvc_scan_chain_forward(struct uvc_video_chain *chain, 142462306a36Sopenharmony_ci struct uvc_entity *entity, struct uvc_entity *prev) 142562306a36Sopenharmony_ci{ 142662306a36Sopenharmony_ci struct uvc_entity *forward; 142762306a36Sopenharmony_ci int found; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci /* Forward scan */ 143062306a36Sopenharmony_ci forward = NULL; 143162306a36Sopenharmony_ci found = 0; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci while (1) { 143462306a36Sopenharmony_ci forward = uvc_entity_by_reference(chain->dev, entity->id, 143562306a36Sopenharmony_ci forward); 143662306a36Sopenharmony_ci if (forward == NULL) 143762306a36Sopenharmony_ci break; 143862306a36Sopenharmony_ci if (forward == prev) 143962306a36Sopenharmony_ci continue; 144062306a36Sopenharmony_ci if (forward->chain.next || forward->chain.prev) { 144162306a36Sopenharmony_ci uvc_dbg(chain->dev, DESCR, 144262306a36Sopenharmony_ci "Found reference to entity %d already in chain\n", 144362306a36Sopenharmony_ci forward->id); 144462306a36Sopenharmony_ci return -EINVAL; 144562306a36Sopenharmony_ci } 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci switch (UVC_ENTITY_TYPE(forward)) { 144862306a36Sopenharmony_ci case UVC_VC_EXTENSION_UNIT: 144962306a36Sopenharmony_ci if (forward->bNrInPins != 1) { 145062306a36Sopenharmony_ci uvc_dbg(chain->dev, DESCR, 145162306a36Sopenharmony_ci "Extension unit %d has more than 1 input pin\n", 145262306a36Sopenharmony_ci forward->id); 145362306a36Sopenharmony_ci return -EINVAL; 145462306a36Sopenharmony_ci } 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci /* 145762306a36Sopenharmony_ci * Some devices reference an output terminal as the 145862306a36Sopenharmony_ci * source of extension units. This is incorrect, as 145962306a36Sopenharmony_ci * output terminals only have an input pin, and thus 146062306a36Sopenharmony_ci * can't be connected to any entity in the forward 146162306a36Sopenharmony_ci * direction. The resulting topology would cause issues 146262306a36Sopenharmony_ci * when registering the media controller graph. To 146362306a36Sopenharmony_ci * avoid this problem, connect the extension unit to 146462306a36Sopenharmony_ci * the source of the output terminal instead. 146562306a36Sopenharmony_ci */ 146662306a36Sopenharmony_ci if (UVC_ENTITY_IS_OTERM(entity)) { 146762306a36Sopenharmony_ci struct uvc_entity *source; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci source = uvc_entity_by_id(chain->dev, 147062306a36Sopenharmony_ci entity->baSourceID[0]); 147162306a36Sopenharmony_ci if (!source) { 147262306a36Sopenharmony_ci uvc_dbg(chain->dev, DESCR, 147362306a36Sopenharmony_ci "Can't connect extension unit %u in chain\n", 147462306a36Sopenharmony_ci forward->id); 147562306a36Sopenharmony_ci break; 147662306a36Sopenharmony_ci } 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci forward->baSourceID[0] = source->id; 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci list_add_tail(&forward->chain, &chain->entities); 148262306a36Sopenharmony_ci if (!found) 148362306a36Sopenharmony_ci uvc_dbg_cont(PROBE, " (->"); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci uvc_dbg_cont(PROBE, " XU %d", forward->id); 148662306a36Sopenharmony_ci found = 1; 148762306a36Sopenharmony_ci break; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci case UVC_OTT_VENDOR_SPECIFIC: 149062306a36Sopenharmony_ci case UVC_OTT_DISPLAY: 149162306a36Sopenharmony_ci case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 149262306a36Sopenharmony_ci case UVC_TT_STREAMING: 149362306a36Sopenharmony_ci if (UVC_ENTITY_IS_ITERM(forward)) { 149462306a36Sopenharmony_ci uvc_dbg(chain->dev, DESCR, 149562306a36Sopenharmony_ci "Unsupported input terminal %u\n", 149662306a36Sopenharmony_ci forward->id); 149762306a36Sopenharmony_ci return -EINVAL; 149862306a36Sopenharmony_ci } 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci if (UVC_ENTITY_IS_OTERM(entity)) { 150162306a36Sopenharmony_ci uvc_dbg(chain->dev, DESCR, 150262306a36Sopenharmony_ci "Unsupported connection between output terminals %u and %u\n", 150362306a36Sopenharmony_ci entity->id, forward->id); 150462306a36Sopenharmony_ci break; 150562306a36Sopenharmony_ci } 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci list_add_tail(&forward->chain, &chain->entities); 150862306a36Sopenharmony_ci if (!found) 150962306a36Sopenharmony_ci uvc_dbg_cont(PROBE, " (->"); 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci uvc_dbg_cont(PROBE, " OT %d", forward->id); 151262306a36Sopenharmony_ci found = 1; 151362306a36Sopenharmony_ci break; 151462306a36Sopenharmony_ci } 151562306a36Sopenharmony_ci } 151662306a36Sopenharmony_ci if (found) 151762306a36Sopenharmony_ci uvc_dbg_cont(PROBE, ")"); 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci return 0; 152062306a36Sopenharmony_ci} 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_cistatic int uvc_scan_chain_backward(struct uvc_video_chain *chain, 152362306a36Sopenharmony_ci struct uvc_entity **_entity) 152462306a36Sopenharmony_ci{ 152562306a36Sopenharmony_ci struct uvc_entity *entity = *_entity; 152662306a36Sopenharmony_ci struct uvc_entity *term; 152762306a36Sopenharmony_ci int id = -EINVAL, i; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci switch (UVC_ENTITY_TYPE(entity)) { 153062306a36Sopenharmony_ci case UVC_VC_EXTENSION_UNIT: 153162306a36Sopenharmony_ci case UVC_VC_PROCESSING_UNIT: 153262306a36Sopenharmony_ci id = entity->baSourceID[0]; 153362306a36Sopenharmony_ci break; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci case UVC_VC_SELECTOR_UNIT: 153662306a36Sopenharmony_ci /* Single-input selector units are ignored. */ 153762306a36Sopenharmony_ci if (entity->bNrInPins == 1) { 153862306a36Sopenharmony_ci id = entity->baSourceID[0]; 153962306a36Sopenharmony_ci break; 154062306a36Sopenharmony_ci } 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci uvc_dbg_cont(PROBE, " <- IT"); 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci chain->selector = entity; 154562306a36Sopenharmony_ci for (i = 0; i < entity->bNrInPins; ++i) { 154662306a36Sopenharmony_ci id = entity->baSourceID[i]; 154762306a36Sopenharmony_ci term = uvc_entity_by_id(chain->dev, id); 154862306a36Sopenharmony_ci if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { 154962306a36Sopenharmony_ci uvc_dbg(chain->dev, DESCR, 155062306a36Sopenharmony_ci "Selector unit %d input %d isn't connected to an input terminal\n", 155162306a36Sopenharmony_ci entity->id, i); 155262306a36Sopenharmony_ci return -1; 155362306a36Sopenharmony_ci } 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci if (term->chain.next || term->chain.prev) { 155662306a36Sopenharmony_ci uvc_dbg(chain->dev, DESCR, 155762306a36Sopenharmony_ci "Found reference to entity %d already in chain\n", 155862306a36Sopenharmony_ci term->id); 155962306a36Sopenharmony_ci return -EINVAL; 156062306a36Sopenharmony_ci } 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci uvc_dbg_cont(PROBE, " %d", term->id); 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci list_add_tail(&term->chain, &chain->entities); 156562306a36Sopenharmony_ci uvc_scan_chain_forward(chain, term, entity); 156662306a36Sopenharmony_ci } 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci uvc_dbg_cont(PROBE, "\n"); 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci id = 0; 157162306a36Sopenharmony_ci break; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci case UVC_ITT_VENDOR_SPECIFIC: 157462306a36Sopenharmony_ci case UVC_ITT_CAMERA: 157562306a36Sopenharmony_ci case UVC_ITT_MEDIA_TRANSPORT_INPUT: 157662306a36Sopenharmony_ci case UVC_OTT_VENDOR_SPECIFIC: 157762306a36Sopenharmony_ci case UVC_OTT_DISPLAY: 157862306a36Sopenharmony_ci case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: 157962306a36Sopenharmony_ci case UVC_TT_STREAMING: 158062306a36Sopenharmony_ci id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0; 158162306a36Sopenharmony_ci break; 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci if (id <= 0) { 158562306a36Sopenharmony_ci *_entity = NULL; 158662306a36Sopenharmony_ci return id; 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci entity = uvc_entity_by_id(chain->dev, id); 159062306a36Sopenharmony_ci if (entity == NULL) { 159162306a36Sopenharmony_ci uvc_dbg(chain->dev, DESCR, 159262306a36Sopenharmony_ci "Found reference to unknown entity %d\n", id); 159362306a36Sopenharmony_ci return -EINVAL; 159462306a36Sopenharmony_ci } 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci *_entity = entity; 159762306a36Sopenharmony_ci return 0; 159862306a36Sopenharmony_ci} 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_cistatic int uvc_scan_chain(struct uvc_video_chain *chain, 160162306a36Sopenharmony_ci struct uvc_entity *term) 160262306a36Sopenharmony_ci{ 160362306a36Sopenharmony_ci struct uvc_entity *entity, *prev; 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci uvc_dbg(chain->dev, PROBE, "Scanning UVC chain:"); 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci entity = term; 160862306a36Sopenharmony_ci prev = NULL; 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci while (entity != NULL) { 161162306a36Sopenharmony_ci /* Entity must not be part of an existing chain */ 161262306a36Sopenharmony_ci if (entity->chain.next || entity->chain.prev) { 161362306a36Sopenharmony_ci uvc_dbg(chain->dev, DESCR, 161462306a36Sopenharmony_ci "Found reference to entity %d already in chain\n", 161562306a36Sopenharmony_ci entity->id); 161662306a36Sopenharmony_ci return -EINVAL; 161762306a36Sopenharmony_ci } 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci /* Process entity */ 162062306a36Sopenharmony_ci if (uvc_scan_chain_entity(chain, entity) < 0) 162162306a36Sopenharmony_ci return -EINVAL; 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci /* Forward scan */ 162462306a36Sopenharmony_ci if (uvc_scan_chain_forward(chain, entity, prev) < 0) 162562306a36Sopenharmony_ci return -EINVAL; 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci /* Backward scan */ 162862306a36Sopenharmony_ci prev = entity; 162962306a36Sopenharmony_ci if (uvc_scan_chain_backward(chain, &entity) < 0) 163062306a36Sopenharmony_ci return -EINVAL; 163162306a36Sopenharmony_ci } 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci return 0; 163462306a36Sopenharmony_ci} 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_cistatic unsigned int uvc_print_terms(struct list_head *terms, u16 dir, 163762306a36Sopenharmony_ci char *buffer) 163862306a36Sopenharmony_ci{ 163962306a36Sopenharmony_ci struct uvc_entity *term; 164062306a36Sopenharmony_ci unsigned int nterms = 0; 164162306a36Sopenharmony_ci char *p = buffer; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci list_for_each_entry(term, terms, chain) { 164462306a36Sopenharmony_ci if (!UVC_ENTITY_IS_TERM(term) || 164562306a36Sopenharmony_ci UVC_TERM_DIRECTION(term) != dir) 164662306a36Sopenharmony_ci continue; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci if (nterms) 164962306a36Sopenharmony_ci p += sprintf(p, ","); 165062306a36Sopenharmony_ci if (++nterms >= 4) { 165162306a36Sopenharmony_ci p += sprintf(p, "..."); 165262306a36Sopenharmony_ci break; 165362306a36Sopenharmony_ci } 165462306a36Sopenharmony_ci p += sprintf(p, "%u", term->id); 165562306a36Sopenharmony_ci } 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci return p - buffer; 165862306a36Sopenharmony_ci} 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_cistatic const char *uvc_print_chain(struct uvc_video_chain *chain) 166162306a36Sopenharmony_ci{ 166262306a36Sopenharmony_ci static char buffer[43]; 166362306a36Sopenharmony_ci char *p = buffer; 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p); 166662306a36Sopenharmony_ci p += sprintf(p, " -> "); 166762306a36Sopenharmony_ci uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p); 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci return buffer; 167062306a36Sopenharmony_ci} 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_cistatic struct uvc_video_chain *uvc_alloc_chain(struct uvc_device *dev) 167362306a36Sopenharmony_ci{ 167462306a36Sopenharmony_ci struct uvc_video_chain *chain; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci chain = kzalloc(sizeof(*chain), GFP_KERNEL); 167762306a36Sopenharmony_ci if (chain == NULL) 167862306a36Sopenharmony_ci return NULL; 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci INIT_LIST_HEAD(&chain->entities); 168162306a36Sopenharmony_ci mutex_init(&chain->ctrl_mutex); 168262306a36Sopenharmony_ci chain->dev = dev; 168362306a36Sopenharmony_ci v4l2_prio_init(&chain->prio); 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci return chain; 168662306a36Sopenharmony_ci} 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci/* 168962306a36Sopenharmony_ci * Fallback heuristic for devices that don't connect units and terminals in a 169062306a36Sopenharmony_ci * valid chain. 169162306a36Sopenharmony_ci * 169262306a36Sopenharmony_ci * Some devices have invalid baSourceID references, causing uvc_scan_chain() 169362306a36Sopenharmony_ci * to fail, but if we just take the entities we can find and put them together 169462306a36Sopenharmony_ci * in the most sensible chain we can think of, turns out they do work anyway. 169562306a36Sopenharmony_ci * Note: This heuristic assumes there is a single chain. 169662306a36Sopenharmony_ci * 169762306a36Sopenharmony_ci * At the time of writing, devices known to have such a broken chain are 169862306a36Sopenharmony_ci * - Acer Integrated Camera (5986:055a) 169962306a36Sopenharmony_ci * - Realtek rtl157a7 (0bda:57a7) 170062306a36Sopenharmony_ci */ 170162306a36Sopenharmony_cistatic int uvc_scan_fallback(struct uvc_device *dev) 170262306a36Sopenharmony_ci{ 170362306a36Sopenharmony_ci struct uvc_video_chain *chain; 170462306a36Sopenharmony_ci struct uvc_entity *iterm = NULL; 170562306a36Sopenharmony_ci struct uvc_entity *oterm = NULL; 170662306a36Sopenharmony_ci struct uvc_entity *entity; 170762306a36Sopenharmony_ci struct uvc_entity *prev; 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci /* 171062306a36Sopenharmony_ci * Start by locating the input and output terminals. We only support 171162306a36Sopenharmony_ci * devices with exactly one of each for now. 171262306a36Sopenharmony_ci */ 171362306a36Sopenharmony_ci list_for_each_entry(entity, &dev->entities, list) { 171462306a36Sopenharmony_ci if (UVC_ENTITY_IS_ITERM(entity)) { 171562306a36Sopenharmony_ci if (iterm) 171662306a36Sopenharmony_ci return -EINVAL; 171762306a36Sopenharmony_ci iterm = entity; 171862306a36Sopenharmony_ci } 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci if (UVC_ENTITY_IS_OTERM(entity)) { 172162306a36Sopenharmony_ci if (oterm) 172262306a36Sopenharmony_ci return -EINVAL; 172362306a36Sopenharmony_ci oterm = entity; 172462306a36Sopenharmony_ci } 172562306a36Sopenharmony_ci } 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci if (iterm == NULL || oterm == NULL) 172862306a36Sopenharmony_ci return -EINVAL; 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci /* Allocate the chain and fill it. */ 173162306a36Sopenharmony_ci chain = uvc_alloc_chain(dev); 173262306a36Sopenharmony_ci if (chain == NULL) 173362306a36Sopenharmony_ci return -ENOMEM; 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci if (uvc_scan_chain_entity(chain, oterm) < 0) 173662306a36Sopenharmony_ci goto error; 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci prev = oterm; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci /* 174162306a36Sopenharmony_ci * Add all Processing and Extension Units with two pads. The order 174262306a36Sopenharmony_ci * doesn't matter much, use reverse list traversal to connect units in 174362306a36Sopenharmony_ci * UVC descriptor order as we build the chain from output to input. This 174462306a36Sopenharmony_ci * leads to units appearing in the order meant by the manufacturer for 174562306a36Sopenharmony_ci * the cameras known to require this heuristic. 174662306a36Sopenharmony_ci */ 174762306a36Sopenharmony_ci list_for_each_entry_reverse(entity, &dev->entities, list) { 174862306a36Sopenharmony_ci if (entity->type != UVC_VC_PROCESSING_UNIT && 174962306a36Sopenharmony_ci entity->type != UVC_VC_EXTENSION_UNIT) 175062306a36Sopenharmony_ci continue; 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci if (entity->num_pads != 2) 175362306a36Sopenharmony_ci continue; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci if (uvc_scan_chain_entity(chain, entity) < 0) 175662306a36Sopenharmony_ci goto error; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci prev->baSourceID[0] = entity->id; 175962306a36Sopenharmony_ci prev = entity; 176062306a36Sopenharmony_ci } 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci if (uvc_scan_chain_entity(chain, iterm) < 0) 176362306a36Sopenharmony_ci goto error; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci prev->baSourceID[0] = iterm->id; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci list_add_tail(&chain->list, &dev->chains); 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci uvc_dbg(dev, PROBE, "Found a video chain by fallback heuristic (%s)\n", 177062306a36Sopenharmony_ci uvc_print_chain(chain)); 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci return 0; 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_cierror: 177562306a36Sopenharmony_ci kfree(chain); 177662306a36Sopenharmony_ci return -EINVAL; 177762306a36Sopenharmony_ci} 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci/* 178062306a36Sopenharmony_ci * Scan the device for video chains and register video devices. 178162306a36Sopenharmony_ci * 178262306a36Sopenharmony_ci * Chains are scanned starting at their output terminals and walked backwards. 178362306a36Sopenharmony_ci */ 178462306a36Sopenharmony_cistatic int uvc_scan_device(struct uvc_device *dev) 178562306a36Sopenharmony_ci{ 178662306a36Sopenharmony_ci struct uvc_video_chain *chain; 178762306a36Sopenharmony_ci struct uvc_entity *term; 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci list_for_each_entry(term, &dev->entities, list) { 179062306a36Sopenharmony_ci if (!UVC_ENTITY_IS_OTERM(term)) 179162306a36Sopenharmony_ci continue; 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci /* 179462306a36Sopenharmony_ci * If the terminal is already included in a chain, skip it. 179562306a36Sopenharmony_ci * This can happen for chains that have multiple output 179662306a36Sopenharmony_ci * terminals, where all output terminals beside the first one 179762306a36Sopenharmony_ci * will be inserted in the chain in forward scans. 179862306a36Sopenharmony_ci */ 179962306a36Sopenharmony_ci if (term->chain.next || term->chain.prev) 180062306a36Sopenharmony_ci continue; 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci chain = uvc_alloc_chain(dev); 180362306a36Sopenharmony_ci if (chain == NULL) 180462306a36Sopenharmony_ci return -ENOMEM; 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci term->flags |= UVC_ENTITY_FLAG_DEFAULT; 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci if (uvc_scan_chain(chain, term) < 0) { 180962306a36Sopenharmony_ci kfree(chain); 181062306a36Sopenharmony_ci continue; 181162306a36Sopenharmony_ci } 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci uvc_dbg(dev, PROBE, "Found a valid video chain (%s)\n", 181462306a36Sopenharmony_ci uvc_print_chain(chain)); 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci list_add_tail(&chain->list, &dev->chains); 181762306a36Sopenharmony_ci } 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci if (list_empty(&dev->chains)) 182062306a36Sopenharmony_ci uvc_scan_fallback(dev); 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci if (list_empty(&dev->chains)) { 182362306a36Sopenharmony_ci dev_info(&dev->udev->dev, "No valid video chain found.\n"); 182462306a36Sopenharmony_ci return -1; 182562306a36Sopenharmony_ci } 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci /* Add GPIO entity to the first chain. */ 182862306a36Sopenharmony_ci if (dev->gpio_unit) { 182962306a36Sopenharmony_ci chain = list_first_entry(&dev->chains, 183062306a36Sopenharmony_ci struct uvc_video_chain, list); 183162306a36Sopenharmony_ci list_add_tail(&dev->gpio_unit->chain, &chain->entities); 183262306a36Sopenharmony_ci } 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci return 0; 183562306a36Sopenharmony_ci} 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci/* ------------------------------------------------------------------------ 183862306a36Sopenharmony_ci * Video device registration and unregistration 183962306a36Sopenharmony_ci */ 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci/* 184262306a36Sopenharmony_ci * Delete the UVC device. 184362306a36Sopenharmony_ci * 184462306a36Sopenharmony_ci * Called by the kernel when the last reference to the uvc_device structure 184562306a36Sopenharmony_ci * is released. 184662306a36Sopenharmony_ci * 184762306a36Sopenharmony_ci * As this function is called after or during disconnect(), all URBs have 184862306a36Sopenharmony_ci * already been cancelled by the USB core. There is no need to kill the 184962306a36Sopenharmony_ci * interrupt URB manually. 185062306a36Sopenharmony_ci */ 185162306a36Sopenharmony_cistatic void uvc_delete(struct kref *kref) 185262306a36Sopenharmony_ci{ 185362306a36Sopenharmony_ci struct uvc_device *dev = container_of(kref, struct uvc_device, ref); 185462306a36Sopenharmony_ci struct list_head *p, *n; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci uvc_status_cleanup(dev); 185762306a36Sopenharmony_ci uvc_ctrl_cleanup_device(dev); 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci usb_put_intf(dev->intf); 186062306a36Sopenharmony_ci usb_put_dev(dev->udev); 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER 186362306a36Sopenharmony_ci media_device_cleanup(&dev->mdev); 186462306a36Sopenharmony_ci#endif 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci list_for_each_safe(p, n, &dev->chains) { 186762306a36Sopenharmony_ci struct uvc_video_chain *chain; 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci chain = list_entry(p, struct uvc_video_chain, list); 187062306a36Sopenharmony_ci kfree(chain); 187162306a36Sopenharmony_ci } 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci list_for_each_safe(p, n, &dev->entities) { 187462306a36Sopenharmony_ci struct uvc_entity *entity; 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci entity = list_entry(p, struct uvc_entity, list); 187762306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER 187862306a36Sopenharmony_ci uvc_mc_cleanup_entity(entity); 187962306a36Sopenharmony_ci#endif 188062306a36Sopenharmony_ci kfree(entity); 188162306a36Sopenharmony_ci } 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci list_for_each_safe(p, n, &dev->streams) { 188462306a36Sopenharmony_ci struct uvc_streaming *streaming; 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci streaming = list_entry(p, struct uvc_streaming, list); 188762306a36Sopenharmony_ci usb_driver_release_interface(&uvc_driver.driver, 188862306a36Sopenharmony_ci streaming->intf); 188962306a36Sopenharmony_ci uvc_stream_delete(streaming); 189062306a36Sopenharmony_ci } 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci kfree(dev); 189362306a36Sopenharmony_ci} 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_cistatic void uvc_release(struct video_device *vdev) 189662306a36Sopenharmony_ci{ 189762306a36Sopenharmony_ci struct uvc_streaming *stream = video_get_drvdata(vdev); 189862306a36Sopenharmony_ci struct uvc_device *dev = stream->dev; 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci kref_put(&dev->ref, uvc_delete); 190162306a36Sopenharmony_ci} 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci/* 190462306a36Sopenharmony_ci * Unregister the video devices. 190562306a36Sopenharmony_ci */ 190662306a36Sopenharmony_cistatic void uvc_unregister_video(struct uvc_device *dev) 190762306a36Sopenharmony_ci{ 190862306a36Sopenharmony_ci struct uvc_streaming *stream; 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci list_for_each_entry(stream, &dev->streams, list) { 191162306a36Sopenharmony_ci if (!video_is_registered(&stream->vdev)) 191262306a36Sopenharmony_ci continue; 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci video_unregister_device(&stream->vdev); 191562306a36Sopenharmony_ci video_unregister_device(&stream->meta.vdev); 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci uvc_debugfs_cleanup_stream(stream); 191862306a36Sopenharmony_ci } 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci uvc_status_unregister(dev); 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci if (dev->vdev.dev) 192362306a36Sopenharmony_ci v4l2_device_unregister(&dev->vdev); 192462306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER 192562306a36Sopenharmony_ci if (media_devnode_is_registered(dev->mdev.devnode)) 192662306a36Sopenharmony_ci media_device_unregister(&dev->mdev); 192762306a36Sopenharmony_ci#endif 192862306a36Sopenharmony_ci} 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ciint uvc_register_video_device(struct uvc_device *dev, 193162306a36Sopenharmony_ci struct uvc_streaming *stream, 193262306a36Sopenharmony_ci struct video_device *vdev, 193362306a36Sopenharmony_ci struct uvc_video_queue *queue, 193462306a36Sopenharmony_ci enum v4l2_buf_type type, 193562306a36Sopenharmony_ci const struct v4l2_file_operations *fops, 193662306a36Sopenharmony_ci const struct v4l2_ioctl_ops *ioctl_ops) 193762306a36Sopenharmony_ci{ 193862306a36Sopenharmony_ci int ret; 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci /* Initialize the video buffers queue. */ 194162306a36Sopenharmony_ci ret = uvc_queue_init(queue, type, !uvc_no_drop_param); 194262306a36Sopenharmony_ci if (ret) 194362306a36Sopenharmony_ci return ret; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci /* Register the device with V4L. */ 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci /* 194862306a36Sopenharmony_ci * We already hold a reference to dev->udev. The video device will be 194962306a36Sopenharmony_ci * unregistered before the reference is released, so we don't need to 195062306a36Sopenharmony_ci * get another one. 195162306a36Sopenharmony_ci */ 195262306a36Sopenharmony_ci vdev->v4l2_dev = &dev->vdev; 195362306a36Sopenharmony_ci vdev->fops = fops; 195462306a36Sopenharmony_ci vdev->ioctl_ops = ioctl_ops; 195562306a36Sopenharmony_ci vdev->release = uvc_release; 195662306a36Sopenharmony_ci vdev->prio = &stream->chain->prio; 195762306a36Sopenharmony_ci if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT) 195862306a36Sopenharmony_ci vdev->vfl_dir = VFL_DIR_TX; 195962306a36Sopenharmony_ci else 196062306a36Sopenharmony_ci vdev->vfl_dir = VFL_DIR_RX; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci switch (type) { 196362306a36Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_CAPTURE: 196462306a36Sopenharmony_ci default: 196562306a36Sopenharmony_ci vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; 196662306a36Sopenharmony_ci break; 196762306a36Sopenharmony_ci case V4L2_BUF_TYPE_VIDEO_OUTPUT: 196862306a36Sopenharmony_ci vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; 196962306a36Sopenharmony_ci break; 197062306a36Sopenharmony_ci case V4L2_BUF_TYPE_META_CAPTURE: 197162306a36Sopenharmony_ci vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING; 197262306a36Sopenharmony_ci break; 197362306a36Sopenharmony_ci } 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci strscpy(vdev->name, dev->name, sizeof(vdev->name)); 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci /* 197862306a36Sopenharmony_ci * Set the driver data before calling video_register_device, otherwise 197962306a36Sopenharmony_ci * the file open() handler might race us. 198062306a36Sopenharmony_ci */ 198162306a36Sopenharmony_ci video_set_drvdata(vdev, stream); 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 198462306a36Sopenharmony_ci if (ret < 0) { 198562306a36Sopenharmony_ci dev_err(&stream->intf->dev, 198662306a36Sopenharmony_ci "Failed to register %s device (%d).\n", 198762306a36Sopenharmony_ci v4l2_type_names[type], ret); 198862306a36Sopenharmony_ci return ret; 198962306a36Sopenharmony_ci } 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci kref_get(&dev->ref); 199262306a36Sopenharmony_ci return 0; 199362306a36Sopenharmony_ci} 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_cistatic int uvc_register_video(struct uvc_device *dev, 199662306a36Sopenharmony_ci struct uvc_streaming *stream) 199762306a36Sopenharmony_ci{ 199862306a36Sopenharmony_ci int ret; 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci /* Initialize the streaming interface with default parameters. */ 200162306a36Sopenharmony_ci ret = uvc_video_init(stream); 200262306a36Sopenharmony_ci if (ret < 0) { 200362306a36Sopenharmony_ci dev_err(&stream->intf->dev, 200462306a36Sopenharmony_ci "Failed to initialize the device (%d).\n", ret); 200562306a36Sopenharmony_ci return ret; 200662306a36Sopenharmony_ci } 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 200962306a36Sopenharmony_ci stream->chain->caps |= V4L2_CAP_VIDEO_CAPTURE 201062306a36Sopenharmony_ci | V4L2_CAP_META_CAPTURE; 201162306a36Sopenharmony_ci else 201262306a36Sopenharmony_ci stream->chain->caps |= V4L2_CAP_VIDEO_OUTPUT; 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci uvc_debugfs_init_stream(stream); 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci /* Register the device with V4L. */ 201762306a36Sopenharmony_ci return uvc_register_video_device(dev, stream, &stream->vdev, 201862306a36Sopenharmony_ci &stream->queue, stream->type, 201962306a36Sopenharmony_ci &uvc_fops, &uvc_ioctl_ops); 202062306a36Sopenharmony_ci} 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci/* 202362306a36Sopenharmony_ci * Register all video devices in all chains. 202462306a36Sopenharmony_ci */ 202562306a36Sopenharmony_cistatic int uvc_register_terms(struct uvc_device *dev, 202662306a36Sopenharmony_ci struct uvc_video_chain *chain) 202762306a36Sopenharmony_ci{ 202862306a36Sopenharmony_ci struct uvc_streaming *stream; 202962306a36Sopenharmony_ci struct uvc_entity *term; 203062306a36Sopenharmony_ci int ret; 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci list_for_each_entry(term, &chain->entities, chain) { 203362306a36Sopenharmony_ci if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) 203462306a36Sopenharmony_ci continue; 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci stream = uvc_stream_by_id(dev, term->id); 203762306a36Sopenharmony_ci if (stream == NULL) { 203862306a36Sopenharmony_ci dev_info(&dev->udev->dev, 203962306a36Sopenharmony_ci "No streaming interface found for terminal %u.", 204062306a36Sopenharmony_ci term->id); 204162306a36Sopenharmony_ci continue; 204262306a36Sopenharmony_ci } 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci stream->chain = chain; 204562306a36Sopenharmony_ci ret = uvc_register_video(dev, stream); 204662306a36Sopenharmony_ci if (ret < 0) 204762306a36Sopenharmony_ci return ret; 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci /* 205062306a36Sopenharmony_ci * Register a metadata node, but ignore a possible failure, 205162306a36Sopenharmony_ci * complete registration of video nodes anyway. 205262306a36Sopenharmony_ci */ 205362306a36Sopenharmony_ci uvc_meta_register(stream); 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci term->vdev = &stream->vdev; 205662306a36Sopenharmony_ci } 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci return 0; 205962306a36Sopenharmony_ci} 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_cistatic int uvc_register_chains(struct uvc_device *dev) 206262306a36Sopenharmony_ci{ 206362306a36Sopenharmony_ci struct uvc_video_chain *chain; 206462306a36Sopenharmony_ci int ret; 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci list_for_each_entry(chain, &dev->chains, list) { 206762306a36Sopenharmony_ci ret = uvc_register_terms(dev, chain); 206862306a36Sopenharmony_ci if (ret < 0) 206962306a36Sopenharmony_ci return ret; 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER 207262306a36Sopenharmony_ci ret = uvc_mc_register_entities(chain); 207362306a36Sopenharmony_ci if (ret < 0) 207462306a36Sopenharmony_ci dev_info(&dev->udev->dev, 207562306a36Sopenharmony_ci "Failed to register entities (%d).\n", ret); 207662306a36Sopenharmony_ci#endif 207762306a36Sopenharmony_ci } 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci return 0; 208062306a36Sopenharmony_ci} 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci/* ------------------------------------------------------------------------ 208362306a36Sopenharmony_ci * USB probe, disconnect, suspend and resume 208462306a36Sopenharmony_ci */ 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_none = { 0 }; 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_cistatic int uvc_probe(struct usb_interface *intf, 208962306a36Sopenharmony_ci const struct usb_device_id *id) 209062306a36Sopenharmony_ci{ 209162306a36Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 209262306a36Sopenharmony_ci struct uvc_device *dev; 209362306a36Sopenharmony_ci const struct uvc_device_info *info = 209462306a36Sopenharmony_ci (const struct uvc_device_info *)id->driver_info; 209562306a36Sopenharmony_ci int function; 209662306a36Sopenharmony_ci int ret; 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci /* Allocate memory for the device and initialize it. */ 209962306a36Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 210062306a36Sopenharmony_ci if (dev == NULL) 210162306a36Sopenharmony_ci return -ENOMEM; 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->entities); 210462306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->chains); 210562306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->streams); 210662306a36Sopenharmony_ci kref_init(&dev->ref); 210762306a36Sopenharmony_ci atomic_set(&dev->nmappings, 0); 210862306a36Sopenharmony_ci mutex_init(&dev->lock); 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci dev->udev = usb_get_dev(udev); 211162306a36Sopenharmony_ci dev->intf = usb_get_intf(intf); 211262306a36Sopenharmony_ci dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; 211362306a36Sopenharmony_ci dev->info = info ? info : &uvc_quirk_none; 211462306a36Sopenharmony_ci dev->quirks = uvc_quirks_param == -1 211562306a36Sopenharmony_ci ? dev->info->quirks : uvc_quirks_param; 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci if (id->idVendor && id->idProduct) 211862306a36Sopenharmony_ci uvc_dbg(dev, PROBE, "Probing known UVC device %s (%04x:%04x)\n", 211962306a36Sopenharmony_ci udev->devpath, id->idVendor, id->idProduct); 212062306a36Sopenharmony_ci else 212162306a36Sopenharmony_ci uvc_dbg(dev, PROBE, "Probing generic UVC device %s\n", 212262306a36Sopenharmony_ci udev->devpath); 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci if (udev->product != NULL) 212562306a36Sopenharmony_ci strscpy(dev->name, udev->product, sizeof(dev->name)); 212662306a36Sopenharmony_ci else 212762306a36Sopenharmony_ci snprintf(dev->name, sizeof(dev->name), 212862306a36Sopenharmony_ci "UVC Camera (%04x:%04x)", 212962306a36Sopenharmony_ci le16_to_cpu(udev->descriptor.idVendor), 213062306a36Sopenharmony_ci le16_to_cpu(udev->descriptor.idProduct)); 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci /* 213362306a36Sopenharmony_ci * Add iFunction or iInterface to names when available as additional 213462306a36Sopenharmony_ci * distinguishers between interfaces. iFunction is prioritized over 213562306a36Sopenharmony_ci * iInterface which matches Windows behavior at the point of writing. 213662306a36Sopenharmony_ci */ 213762306a36Sopenharmony_ci if (intf->intf_assoc && intf->intf_assoc->iFunction != 0) 213862306a36Sopenharmony_ci function = intf->intf_assoc->iFunction; 213962306a36Sopenharmony_ci else 214062306a36Sopenharmony_ci function = intf->cur_altsetting->desc.iInterface; 214162306a36Sopenharmony_ci if (function != 0) { 214262306a36Sopenharmony_ci size_t len; 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci strlcat(dev->name, ": ", sizeof(dev->name)); 214562306a36Sopenharmony_ci len = strlen(dev->name); 214662306a36Sopenharmony_ci usb_string(udev, function, dev->name + len, 214762306a36Sopenharmony_ci sizeof(dev->name) - len); 214862306a36Sopenharmony_ci } 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci /* Initialize the media device. */ 215162306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER 215262306a36Sopenharmony_ci dev->mdev.dev = &intf->dev; 215362306a36Sopenharmony_ci strscpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model)); 215462306a36Sopenharmony_ci if (udev->serial) 215562306a36Sopenharmony_ci strscpy(dev->mdev.serial, udev->serial, 215662306a36Sopenharmony_ci sizeof(dev->mdev.serial)); 215762306a36Sopenharmony_ci usb_make_path(udev, dev->mdev.bus_info, sizeof(dev->mdev.bus_info)); 215862306a36Sopenharmony_ci dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); 215962306a36Sopenharmony_ci media_device_init(&dev->mdev); 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci dev->vdev.mdev = &dev->mdev; 216262306a36Sopenharmony_ci#endif 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci /* Parse the Video Class control descriptor. */ 216562306a36Sopenharmony_ci if (uvc_parse_control(dev) < 0) { 216662306a36Sopenharmony_ci uvc_dbg(dev, PROBE, "Unable to parse UVC descriptors\n"); 216762306a36Sopenharmony_ci goto error; 216862306a36Sopenharmony_ci } 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci /* Parse the associated GPIOs. */ 217162306a36Sopenharmony_ci if (uvc_gpio_parse(dev) < 0) { 217262306a36Sopenharmony_ci uvc_dbg(dev, PROBE, "Unable to parse UVC GPIOs\n"); 217362306a36Sopenharmony_ci goto error; 217462306a36Sopenharmony_ci } 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci dev_info(&dev->udev->dev, "Found UVC %u.%02x device %s (%04x:%04x)\n", 217762306a36Sopenharmony_ci dev->uvc_version >> 8, dev->uvc_version & 0xff, 217862306a36Sopenharmony_ci udev->product ? udev->product : "<unnamed>", 217962306a36Sopenharmony_ci le16_to_cpu(udev->descriptor.idVendor), 218062306a36Sopenharmony_ci le16_to_cpu(udev->descriptor.idProduct)); 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci if (dev->quirks != dev->info->quirks) { 218362306a36Sopenharmony_ci dev_info(&dev->udev->dev, 218462306a36Sopenharmony_ci "Forcing device quirks to 0x%x by module parameter for testing purpose.\n", 218562306a36Sopenharmony_ci dev->quirks); 218662306a36Sopenharmony_ci dev_info(&dev->udev->dev, 218762306a36Sopenharmony_ci "Please report required quirks to the linux-media mailing list.\n"); 218862306a36Sopenharmony_ci } 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci if (dev->info->uvc_version) { 219162306a36Sopenharmony_ci dev->uvc_version = dev->info->uvc_version; 219262306a36Sopenharmony_ci dev_info(&dev->udev->dev, "Forcing UVC version to %u.%02x\n", 219362306a36Sopenharmony_ci dev->uvc_version >> 8, dev->uvc_version & 0xff); 219462306a36Sopenharmony_ci } 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci /* Register the V4L2 device. */ 219762306a36Sopenharmony_ci if (v4l2_device_register(&intf->dev, &dev->vdev) < 0) 219862306a36Sopenharmony_ci goto error; 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci /* Scan the device for video chains. */ 220162306a36Sopenharmony_ci if (uvc_scan_device(dev) < 0) 220262306a36Sopenharmony_ci goto error; 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci /* Initialize controls. */ 220562306a36Sopenharmony_ci if (uvc_ctrl_init_device(dev) < 0) 220662306a36Sopenharmony_ci goto error; 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci /* Register video device nodes. */ 220962306a36Sopenharmony_ci if (uvc_register_chains(dev) < 0) 221062306a36Sopenharmony_ci goto error; 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci#ifdef CONFIG_MEDIA_CONTROLLER 221362306a36Sopenharmony_ci /* Register the media device node */ 221462306a36Sopenharmony_ci if (media_device_register(&dev->mdev) < 0) 221562306a36Sopenharmony_ci goto error; 221662306a36Sopenharmony_ci#endif 221762306a36Sopenharmony_ci /* Save our data pointer in the interface data. */ 221862306a36Sopenharmony_ci usb_set_intfdata(intf, dev); 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci /* Initialize the interrupt URB. */ 222162306a36Sopenharmony_ci ret = uvc_status_init(dev); 222262306a36Sopenharmony_ci if (ret < 0) { 222362306a36Sopenharmony_ci dev_info(&dev->udev->dev, 222462306a36Sopenharmony_ci "Unable to initialize the status endpoint (%d), status interrupt will not be supported.\n", 222562306a36Sopenharmony_ci ret); 222662306a36Sopenharmony_ci } 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci ret = uvc_gpio_init_irq(dev); 222962306a36Sopenharmony_ci if (ret < 0) { 223062306a36Sopenharmony_ci dev_err(&dev->udev->dev, 223162306a36Sopenharmony_ci "Unable to request privacy GPIO IRQ (%d)\n", ret); 223262306a36Sopenharmony_ci goto error; 223362306a36Sopenharmony_ci } 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci uvc_dbg(dev, PROBE, "UVC device initialized\n"); 223662306a36Sopenharmony_ci usb_enable_autosuspend(udev); 223762306a36Sopenharmony_ci return 0; 223862306a36Sopenharmony_ci 223962306a36Sopenharmony_cierror: 224062306a36Sopenharmony_ci uvc_unregister_video(dev); 224162306a36Sopenharmony_ci kref_put(&dev->ref, uvc_delete); 224262306a36Sopenharmony_ci return -ENODEV; 224362306a36Sopenharmony_ci} 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_cistatic void uvc_disconnect(struct usb_interface *intf) 224662306a36Sopenharmony_ci{ 224762306a36Sopenharmony_ci struct uvc_device *dev = usb_get_intfdata(intf); 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci /* 225062306a36Sopenharmony_ci * Set the USB interface data to NULL. This can be done outside the 225162306a36Sopenharmony_ci * lock, as there's no other reader. 225262306a36Sopenharmony_ci */ 225362306a36Sopenharmony_ci usb_set_intfdata(intf, NULL); 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci if (intf->cur_altsetting->desc.bInterfaceSubClass == 225662306a36Sopenharmony_ci UVC_SC_VIDEOSTREAMING) 225762306a36Sopenharmony_ci return; 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci uvc_unregister_video(dev); 226062306a36Sopenharmony_ci kref_put(&dev->ref, uvc_delete); 226162306a36Sopenharmony_ci} 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_cistatic int uvc_suspend(struct usb_interface *intf, pm_message_t message) 226462306a36Sopenharmony_ci{ 226562306a36Sopenharmony_ci struct uvc_device *dev = usb_get_intfdata(intf); 226662306a36Sopenharmony_ci struct uvc_streaming *stream; 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_ci uvc_dbg(dev, SUSPEND, "Suspending interface %u\n", 226962306a36Sopenharmony_ci intf->cur_altsetting->desc.bInterfaceNumber); 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci /* Controls are cached on the fly so they don't need to be saved. */ 227262306a36Sopenharmony_ci if (intf->cur_altsetting->desc.bInterfaceSubClass == 227362306a36Sopenharmony_ci UVC_SC_VIDEOCONTROL) { 227462306a36Sopenharmony_ci mutex_lock(&dev->lock); 227562306a36Sopenharmony_ci if (dev->users) 227662306a36Sopenharmony_ci uvc_status_stop(dev); 227762306a36Sopenharmony_ci mutex_unlock(&dev->lock); 227862306a36Sopenharmony_ci return 0; 227962306a36Sopenharmony_ci } 228062306a36Sopenharmony_ci 228162306a36Sopenharmony_ci list_for_each_entry(stream, &dev->streams, list) { 228262306a36Sopenharmony_ci if (stream->intf == intf) 228362306a36Sopenharmony_ci return uvc_video_suspend(stream); 228462306a36Sopenharmony_ci } 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ci uvc_dbg(dev, SUSPEND, 228762306a36Sopenharmony_ci "Suspend: video streaming USB interface mismatch\n"); 228862306a36Sopenharmony_ci return -EINVAL; 228962306a36Sopenharmony_ci} 229062306a36Sopenharmony_ci 229162306a36Sopenharmony_cistatic int __uvc_resume(struct usb_interface *intf, int reset) 229262306a36Sopenharmony_ci{ 229362306a36Sopenharmony_ci struct uvc_device *dev = usb_get_intfdata(intf); 229462306a36Sopenharmony_ci struct uvc_streaming *stream; 229562306a36Sopenharmony_ci int ret = 0; 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci uvc_dbg(dev, SUSPEND, "Resuming interface %u\n", 229862306a36Sopenharmony_ci intf->cur_altsetting->desc.bInterfaceNumber); 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci if (intf->cur_altsetting->desc.bInterfaceSubClass == 230162306a36Sopenharmony_ci UVC_SC_VIDEOCONTROL) { 230262306a36Sopenharmony_ci if (reset) { 230362306a36Sopenharmony_ci ret = uvc_ctrl_restore_values(dev); 230462306a36Sopenharmony_ci if (ret < 0) 230562306a36Sopenharmony_ci return ret; 230662306a36Sopenharmony_ci } 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci mutex_lock(&dev->lock); 230962306a36Sopenharmony_ci if (dev->users) 231062306a36Sopenharmony_ci ret = uvc_status_start(dev, GFP_NOIO); 231162306a36Sopenharmony_ci mutex_unlock(&dev->lock); 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci return ret; 231462306a36Sopenharmony_ci } 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci list_for_each_entry(stream, &dev->streams, list) { 231762306a36Sopenharmony_ci if (stream->intf == intf) { 231862306a36Sopenharmony_ci ret = uvc_video_resume(stream, reset); 231962306a36Sopenharmony_ci if (ret < 0) 232062306a36Sopenharmony_ci uvc_queue_streamoff(&stream->queue, 232162306a36Sopenharmony_ci stream->queue.queue.type); 232262306a36Sopenharmony_ci return ret; 232362306a36Sopenharmony_ci } 232462306a36Sopenharmony_ci } 232562306a36Sopenharmony_ci 232662306a36Sopenharmony_ci uvc_dbg(dev, SUSPEND, 232762306a36Sopenharmony_ci "Resume: video streaming USB interface mismatch\n"); 232862306a36Sopenharmony_ci return -EINVAL; 232962306a36Sopenharmony_ci} 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_cistatic int uvc_resume(struct usb_interface *intf) 233262306a36Sopenharmony_ci{ 233362306a36Sopenharmony_ci return __uvc_resume(intf, 0); 233462306a36Sopenharmony_ci} 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_cistatic int uvc_reset_resume(struct usb_interface *intf) 233762306a36Sopenharmony_ci{ 233862306a36Sopenharmony_ci return __uvc_resume(intf, 1); 233962306a36Sopenharmony_ci} 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci/* ------------------------------------------------------------------------ 234262306a36Sopenharmony_ci * Module parameters 234362306a36Sopenharmony_ci */ 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_cistatic int uvc_clock_param_get(char *buffer, const struct kernel_param *kp) 234662306a36Sopenharmony_ci{ 234762306a36Sopenharmony_ci if (uvc_clock_param == CLOCK_MONOTONIC) 234862306a36Sopenharmony_ci return sprintf(buffer, "CLOCK_MONOTONIC"); 234962306a36Sopenharmony_ci else 235062306a36Sopenharmony_ci return sprintf(buffer, "CLOCK_REALTIME"); 235162306a36Sopenharmony_ci} 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_cistatic int uvc_clock_param_set(const char *val, const struct kernel_param *kp) 235462306a36Sopenharmony_ci{ 235562306a36Sopenharmony_ci if (strncasecmp(val, "clock_", strlen("clock_")) == 0) 235662306a36Sopenharmony_ci val += strlen("clock_"); 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci if (strcasecmp(val, "monotonic") == 0) 235962306a36Sopenharmony_ci uvc_clock_param = CLOCK_MONOTONIC; 236062306a36Sopenharmony_ci else if (strcasecmp(val, "realtime") == 0) 236162306a36Sopenharmony_ci uvc_clock_param = CLOCK_REALTIME; 236262306a36Sopenharmony_ci else 236362306a36Sopenharmony_ci return -EINVAL; 236462306a36Sopenharmony_ci 236562306a36Sopenharmony_ci return 0; 236662306a36Sopenharmony_ci} 236762306a36Sopenharmony_ci 236862306a36Sopenharmony_cimodule_param_call(clock, uvc_clock_param_set, uvc_clock_param_get, 236962306a36Sopenharmony_ci &uvc_clock_param, 0644); 237062306a36Sopenharmony_ciMODULE_PARM_DESC(clock, "Video buffers timestamp clock"); 237162306a36Sopenharmony_cimodule_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, 0644); 237262306a36Sopenharmony_ciMODULE_PARM_DESC(hwtimestamps, "Use hardware timestamps"); 237362306a36Sopenharmony_cimodule_param_named(nodrop, uvc_no_drop_param, uint, 0644); 237462306a36Sopenharmony_ciMODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); 237562306a36Sopenharmony_cimodule_param_named(quirks, uvc_quirks_param, uint, 0644); 237662306a36Sopenharmony_ciMODULE_PARM_DESC(quirks, "Forced device quirks"); 237762306a36Sopenharmony_cimodule_param_named(trace, uvc_dbg_param, uint, 0644); 237862306a36Sopenharmony_ciMODULE_PARM_DESC(trace, "Trace level bitmask"); 237962306a36Sopenharmony_cimodule_param_named(timeout, uvc_timeout_param, uint, 0644); 238062306a36Sopenharmony_ciMODULE_PARM_DESC(timeout, "Streaming control requests timeout"); 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci/* ------------------------------------------------------------------------ 238362306a36Sopenharmony_ci * Driver initialization and cleanup 238462306a36Sopenharmony_ci */ 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_cistatic const struct uvc_device_info uvc_ctrl_power_line_limited = { 238762306a36Sopenharmony_ci .mappings = (const struct uvc_control_mapping *[]) { 238862306a36Sopenharmony_ci &uvc_ctrl_power_line_mapping_limited, 238962306a36Sopenharmony_ci NULL, /* Sentinel */ 239062306a36Sopenharmony_ci }, 239162306a36Sopenharmony_ci}; 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_cistatic const struct uvc_device_info uvc_ctrl_power_line_uvc11 = { 239462306a36Sopenharmony_ci .mappings = (const struct uvc_control_mapping *[]) { 239562306a36Sopenharmony_ci &uvc_ctrl_power_line_mapping_uvc11, 239662306a36Sopenharmony_ci NULL, /* Sentinel */ 239762306a36Sopenharmony_ci }, 239862306a36Sopenharmony_ci}; 239962306a36Sopenharmony_ci 240062306a36Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_probe_minmax = { 240162306a36Sopenharmony_ci .quirks = UVC_QUIRK_PROBE_MINMAX, 240262306a36Sopenharmony_ci}; 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_fix_bandwidth = { 240562306a36Sopenharmony_ci .quirks = UVC_QUIRK_FIX_BANDWIDTH, 240662306a36Sopenharmony_ci}; 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_probe_def = { 240962306a36Sopenharmony_ci .quirks = UVC_QUIRK_PROBE_DEF, 241062306a36Sopenharmony_ci}; 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_stream_no_fid = { 241362306a36Sopenharmony_ci .quirks = UVC_QUIRK_STREAM_NO_FID, 241462306a36Sopenharmony_ci}; 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_cistatic const struct uvc_device_info uvc_quirk_force_y8 = { 241762306a36Sopenharmony_ci .quirks = UVC_QUIRK_FORCE_Y8, 241862306a36Sopenharmony_ci}; 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci#define UVC_INFO_QUIRK(q) (kernel_ulong_t)&(struct uvc_device_info){.quirks = q} 242162306a36Sopenharmony_ci#define UVC_INFO_META(m) (kernel_ulong_t)&(struct uvc_device_info) \ 242262306a36Sopenharmony_ci {.meta_format = m} 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_ci/* 242562306a36Sopenharmony_ci * The Logitech cameras listed below have their interface class set to 242662306a36Sopenharmony_ci * VENDOR_SPEC because they don't announce themselves as UVC devices, even 242762306a36Sopenharmony_ci * though they are compliant. 242862306a36Sopenharmony_ci */ 242962306a36Sopenharmony_cistatic const struct usb_device_id uvc_ids[] = { 243062306a36Sopenharmony_ci /* Quanta USB2.0 HD UVC Webcam */ 243162306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 243262306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 243362306a36Sopenharmony_ci .idVendor = 0x0408, 243462306a36Sopenharmony_ci .idProduct = 0x3090, 243562306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 243662306a36Sopenharmony_ci .bInterfaceSubClass = 1, 243762306a36Sopenharmony_ci .bInterfaceProtocol = 0, 243862306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 243962306a36Sopenharmony_ci /* Quanta USB2.0 HD UVC Webcam */ 244062306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 244162306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 244262306a36Sopenharmony_ci .idVendor = 0x0408, 244362306a36Sopenharmony_ci .idProduct = 0x4030, 244462306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 244562306a36Sopenharmony_ci .bInterfaceSubClass = 1, 244662306a36Sopenharmony_ci .bInterfaceProtocol = 0, 244762306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 244862306a36Sopenharmony_ci /* Quanta USB2.0 HD UVC Webcam */ 244962306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 245062306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 245162306a36Sopenharmony_ci .idVendor = 0x0408, 245262306a36Sopenharmony_ci .idProduct = 0x4034, 245362306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 245462306a36Sopenharmony_ci .bInterfaceSubClass = 1, 245562306a36Sopenharmony_ci .bInterfaceProtocol = UVC_PC_PROTOCOL_15, 245662306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 245762306a36Sopenharmony_ci /* LogiLink Wireless Webcam */ 245862306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 245962306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 246062306a36Sopenharmony_ci .idVendor = 0x0416, 246162306a36Sopenharmony_ci .idProduct = 0xa91a, 246262306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 246362306a36Sopenharmony_ci .bInterfaceSubClass = 1, 246462306a36Sopenharmony_ci .bInterfaceProtocol = 0, 246562306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 246662306a36Sopenharmony_ci /* Genius eFace 2025 */ 246762306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 246862306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 246962306a36Sopenharmony_ci .idVendor = 0x0458, 247062306a36Sopenharmony_ci .idProduct = 0x706e, 247162306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 247262306a36Sopenharmony_ci .bInterfaceSubClass = 1, 247362306a36Sopenharmony_ci .bInterfaceProtocol = 0, 247462306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 247562306a36Sopenharmony_ci /* Microsoft Lifecam NX-6000 */ 247662306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 247762306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 247862306a36Sopenharmony_ci .idVendor = 0x045e, 247962306a36Sopenharmony_ci .idProduct = 0x00f8, 248062306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 248162306a36Sopenharmony_ci .bInterfaceSubClass = 1, 248262306a36Sopenharmony_ci .bInterfaceProtocol = 0, 248362306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 248462306a36Sopenharmony_ci /* Microsoft Lifecam NX-3000 */ 248562306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 248662306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 248762306a36Sopenharmony_ci .idVendor = 0x045e, 248862306a36Sopenharmony_ci .idProduct = 0x0721, 248962306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 249062306a36Sopenharmony_ci .bInterfaceSubClass = 1, 249162306a36Sopenharmony_ci .bInterfaceProtocol = 0, 249262306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 249362306a36Sopenharmony_ci /* Microsoft Lifecam VX-7000 */ 249462306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 249562306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 249662306a36Sopenharmony_ci .idVendor = 0x045e, 249762306a36Sopenharmony_ci .idProduct = 0x0723, 249862306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 249962306a36Sopenharmony_ci .bInterfaceSubClass = 1, 250062306a36Sopenharmony_ci .bInterfaceProtocol = 0, 250162306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 250262306a36Sopenharmony_ci /* Logitech, Webcam C910 */ 250362306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 250462306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 250562306a36Sopenharmony_ci .idVendor = 0x046d, 250662306a36Sopenharmony_ci .idProduct = 0x0821, 250762306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 250862306a36Sopenharmony_ci .bInterfaceSubClass = 1, 250962306a36Sopenharmony_ci .bInterfaceProtocol = 0, 251062306a36Sopenharmony_ci .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_WAKE_AUTOSUSPEND)}, 251162306a36Sopenharmony_ci /* Logitech, Webcam B910 */ 251262306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 251362306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 251462306a36Sopenharmony_ci .idVendor = 0x046d, 251562306a36Sopenharmony_ci .idProduct = 0x0823, 251662306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 251762306a36Sopenharmony_ci .bInterfaceSubClass = 1, 251862306a36Sopenharmony_ci .bInterfaceProtocol = 0, 251962306a36Sopenharmony_ci .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_WAKE_AUTOSUSPEND)}, 252062306a36Sopenharmony_ci /* Logitech Quickcam Fusion */ 252162306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 252262306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 252362306a36Sopenharmony_ci .idVendor = 0x046d, 252462306a36Sopenharmony_ci .idProduct = 0x08c1, 252562306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 252662306a36Sopenharmony_ci .bInterfaceSubClass = 1, 252762306a36Sopenharmony_ci .bInterfaceProtocol = 0 }, 252862306a36Sopenharmony_ci /* Logitech Quickcam Orbit MP */ 252962306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 253062306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 253162306a36Sopenharmony_ci .idVendor = 0x046d, 253262306a36Sopenharmony_ci .idProduct = 0x08c2, 253362306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 253462306a36Sopenharmony_ci .bInterfaceSubClass = 1, 253562306a36Sopenharmony_ci .bInterfaceProtocol = 0 }, 253662306a36Sopenharmony_ci /* Logitech Quickcam Pro for Notebook */ 253762306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 253862306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 253962306a36Sopenharmony_ci .idVendor = 0x046d, 254062306a36Sopenharmony_ci .idProduct = 0x08c3, 254162306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 254262306a36Sopenharmony_ci .bInterfaceSubClass = 1, 254362306a36Sopenharmony_ci .bInterfaceProtocol = 0 }, 254462306a36Sopenharmony_ci /* Logitech Quickcam Pro 5000 */ 254562306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 254662306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 254762306a36Sopenharmony_ci .idVendor = 0x046d, 254862306a36Sopenharmony_ci .idProduct = 0x08c5, 254962306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 255062306a36Sopenharmony_ci .bInterfaceSubClass = 1, 255162306a36Sopenharmony_ci .bInterfaceProtocol = 0 }, 255262306a36Sopenharmony_ci /* Logitech Quickcam OEM Dell Notebook */ 255362306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 255462306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 255562306a36Sopenharmony_ci .idVendor = 0x046d, 255662306a36Sopenharmony_ci .idProduct = 0x08c6, 255762306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 255862306a36Sopenharmony_ci .bInterfaceSubClass = 1, 255962306a36Sopenharmony_ci .bInterfaceProtocol = 0 }, 256062306a36Sopenharmony_ci /* Logitech Quickcam OEM Cisco VT Camera II */ 256162306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 256262306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 256362306a36Sopenharmony_ci .idVendor = 0x046d, 256462306a36Sopenharmony_ci .idProduct = 0x08c7, 256562306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 256662306a36Sopenharmony_ci .bInterfaceSubClass = 1, 256762306a36Sopenharmony_ci .bInterfaceProtocol = 0 }, 256862306a36Sopenharmony_ci /* Logitech HD Pro Webcam C920 */ 256962306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 257062306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 257162306a36Sopenharmony_ci .idVendor = 0x046d, 257262306a36Sopenharmony_ci .idProduct = 0x082d, 257362306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 257462306a36Sopenharmony_ci .bInterfaceSubClass = 1, 257562306a36Sopenharmony_ci .bInterfaceProtocol = 0, 257662306a36Sopenharmony_ci .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTORE_CTRLS_ON_INIT) }, 257762306a36Sopenharmony_ci /* Chicony CNF7129 (Asus EEE 100HE) */ 257862306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 257962306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 258062306a36Sopenharmony_ci .idVendor = 0x04f2, 258162306a36Sopenharmony_ci .idProduct = 0xb071, 258262306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 258362306a36Sopenharmony_ci .bInterfaceSubClass = 1, 258462306a36Sopenharmony_ci .bInterfaceProtocol = 0, 258562306a36Sopenharmony_ci .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_RESTRICT_FRAME_RATE) }, 258662306a36Sopenharmony_ci /* Chicony EasyCamera */ 258762306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 258862306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 258962306a36Sopenharmony_ci .idVendor = 0x04f2, 259062306a36Sopenharmony_ci .idProduct = 0xb5eb, 259162306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 259262306a36Sopenharmony_ci .bInterfaceSubClass = 1, 259362306a36Sopenharmony_ci .bInterfaceProtocol = 0, 259462306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 259562306a36Sopenharmony_ci /* Chicony Electronics Co., Ltd Integrated Camera */ 259662306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 259762306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 259862306a36Sopenharmony_ci .idVendor = 0x04f2, 259962306a36Sopenharmony_ci .idProduct = 0xb67c, 260062306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 260162306a36Sopenharmony_ci .bInterfaceSubClass = 1, 260262306a36Sopenharmony_ci .bInterfaceProtocol = UVC_PC_PROTOCOL_15, 260362306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 }, 260462306a36Sopenharmony_ci /* Chicony EasyCamera */ 260562306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 260662306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 260762306a36Sopenharmony_ci .idVendor = 0x04f2, 260862306a36Sopenharmony_ci .idProduct = 0xb6ba, 260962306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 261062306a36Sopenharmony_ci .bInterfaceSubClass = 1, 261162306a36Sopenharmony_ci .bInterfaceProtocol = 0, 261262306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 261362306a36Sopenharmony_ci /* Chicony EasyCamera */ 261462306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 261562306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 261662306a36Sopenharmony_ci .idVendor = 0x04f2, 261762306a36Sopenharmony_ci .idProduct = 0xb746, 261862306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 261962306a36Sopenharmony_ci .bInterfaceSubClass = 1, 262062306a36Sopenharmony_ci .bInterfaceProtocol = 0, 262162306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 262262306a36Sopenharmony_ci /* Alcor Micro AU3820 (Future Boy PC USB Webcam) */ 262362306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 262462306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 262562306a36Sopenharmony_ci .idVendor = 0x058f, 262662306a36Sopenharmony_ci .idProduct = 0x3820, 262762306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 262862306a36Sopenharmony_ci .bInterfaceSubClass = 1, 262962306a36Sopenharmony_ci .bInterfaceProtocol = 0, 263062306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 263162306a36Sopenharmony_ci /* Dell XPS m1530 */ 263262306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 263362306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 263462306a36Sopenharmony_ci .idVendor = 0x05a9, 263562306a36Sopenharmony_ci .idProduct = 0x2640, 263662306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 263762306a36Sopenharmony_ci .bInterfaceSubClass = 1, 263862306a36Sopenharmony_ci .bInterfaceProtocol = 0, 263962306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 264062306a36Sopenharmony_ci /* Dell SP2008WFP Monitor */ 264162306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 264262306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 264362306a36Sopenharmony_ci .idVendor = 0x05a9, 264462306a36Sopenharmony_ci .idProduct = 0x2641, 264562306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 264662306a36Sopenharmony_ci .bInterfaceSubClass = 1, 264762306a36Sopenharmony_ci .bInterfaceProtocol = 0, 264862306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 264962306a36Sopenharmony_ci /* Dell Alienware X51 */ 265062306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 265162306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 265262306a36Sopenharmony_ci .idVendor = 0x05a9, 265362306a36Sopenharmony_ci .idProduct = 0x2643, 265462306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 265562306a36Sopenharmony_ci .bInterfaceSubClass = 1, 265662306a36Sopenharmony_ci .bInterfaceProtocol = 0, 265762306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 265862306a36Sopenharmony_ci /* Dell Studio Hybrid 140g (OmniVision webcam) */ 265962306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 266062306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 266162306a36Sopenharmony_ci .idVendor = 0x05a9, 266262306a36Sopenharmony_ci .idProduct = 0x264a, 266362306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 266462306a36Sopenharmony_ci .bInterfaceSubClass = 1, 266562306a36Sopenharmony_ci .bInterfaceProtocol = 0, 266662306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 266762306a36Sopenharmony_ci /* Dell XPS M1330 (OmniVision OV7670 webcam) */ 266862306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 266962306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 267062306a36Sopenharmony_ci .idVendor = 0x05a9, 267162306a36Sopenharmony_ci .idProduct = 0x7670, 267262306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 267362306a36Sopenharmony_ci .bInterfaceSubClass = 1, 267462306a36Sopenharmony_ci .bInterfaceProtocol = 0, 267562306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 267662306a36Sopenharmony_ci /* Apple Built-In iSight */ 267762306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 267862306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 267962306a36Sopenharmony_ci .idVendor = 0x05ac, 268062306a36Sopenharmony_ci .idProduct = 0x8501, 268162306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 268262306a36Sopenharmony_ci .bInterfaceSubClass = 1, 268362306a36Sopenharmony_ci .bInterfaceProtocol = 0, 268462306a36Sopenharmony_ci .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 268562306a36Sopenharmony_ci | UVC_QUIRK_BUILTIN_ISIGHT) }, 268662306a36Sopenharmony_ci /* Apple FaceTime HD Camera (Built-In) */ 268762306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 268862306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 268962306a36Sopenharmony_ci .idVendor = 0x05ac, 269062306a36Sopenharmony_ci .idProduct = 0x8514, 269162306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 269262306a36Sopenharmony_ci .bInterfaceSubClass = 1, 269362306a36Sopenharmony_ci .bInterfaceProtocol = 0, 269462306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 269562306a36Sopenharmony_ci /* Apple Built-In iSight via iBridge */ 269662306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 269762306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 269862306a36Sopenharmony_ci .idVendor = 0x05ac, 269962306a36Sopenharmony_ci .idProduct = 0x8600, 270062306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 270162306a36Sopenharmony_ci .bInterfaceSubClass = 1, 270262306a36Sopenharmony_ci .bInterfaceProtocol = 0, 270362306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 270462306a36Sopenharmony_ci /* Foxlink ("HP Webcam" on HP Mini 5103) */ 270562306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 270662306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 270762306a36Sopenharmony_ci .idVendor = 0x05c8, 270862306a36Sopenharmony_ci .idProduct = 0x0403, 270962306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 271062306a36Sopenharmony_ci .bInterfaceSubClass = 1, 271162306a36Sopenharmony_ci .bInterfaceProtocol = 0, 271262306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 271362306a36Sopenharmony_ci /* Genesys Logic USB 2.0 PC Camera */ 271462306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 271562306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 271662306a36Sopenharmony_ci .idVendor = 0x05e3, 271762306a36Sopenharmony_ci .idProduct = 0x0505, 271862306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 271962306a36Sopenharmony_ci .bInterfaceSubClass = 1, 272062306a36Sopenharmony_ci .bInterfaceProtocol = 0, 272162306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 272262306a36Sopenharmony_ci /* Hercules Classic Silver */ 272362306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 272462306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 272562306a36Sopenharmony_ci .idVendor = 0x06f8, 272662306a36Sopenharmony_ci .idProduct = 0x300c, 272762306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 272862306a36Sopenharmony_ci .bInterfaceSubClass = 1, 272962306a36Sopenharmony_ci .bInterfaceProtocol = 0, 273062306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 273162306a36Sopenharmony_ci /* ViMicro Vega */ 273262306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 273362306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 273462306a36Sopenharmony_ci .idVendor = 0x0ac8, 273562306a36Sopenharmony_ci .idProduct = 0x332d, 273662306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 273762306a36Sopenharmony_ci .bInterfaceSubClass = 1, 273862306a36Sopenharmony_ci .bInterfaceProtocol = 0, 273962306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 274062306a36Sopenharmony_ci /* ViMicro - Minoru3D */ 274162306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 274262306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 274362306a36Sopenharmony_ci .idVendor = 0x0ac8, 274462306a36Sopenharmony_ci .idProduct = 0x3410, 274562306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 274662306a36Sopenharmony_ci .bInterfaceSubClass = 1, 274762306a36Sopenharmony_ci .bInterfaceProtocol = 0, 274862306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 274962306a36Sopenharmony_ci /* ViMicro Venus - Minoru3D */ 275062306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 275162306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 275262306a36Sopenharmony_ci .idVendor = 0x0ac8, 275362306a36Sopenharmony_ci .idProduct = 0x3420, 275462306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 275562306a36Sopenharmony_ci .bInterfaceSubClass = 1, 275662306a36Sopenharmony_ci .bInterfaceProtocol = 0, 275762306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_fix_bandwidth }, 275862306a36Sopenharmony_ci /* Ophir Optronics - SPCAM 620U */ 275962306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 276062306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 276162306a36Sopenharmony_ci .idVendor = 0x0bd3, 276262306a36Sopenharmony_ci .idProduct = 0x0555, 276362306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 276462306a36Sopenharmony_ci .bInterfaceSubClass = 1, 276562306a36Sopenharmony_ci .bInterfaceProtocol = 0, 276662306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 276762306a36Sopenharmony_ci /* MT6227 */ 276862306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 276962306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 277062306a36Sopenharmony_ci .idVendor = 0x0e8d, 277162306a36Sopenharmony_ci .idProduct = 0x0004, 277262306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 277362306a36Sopenharmony_ci .bInterfaceSubClass = 1, 277462306a36Sopenharmony_ci .bInterfaceProtocol = 0, 277562306a36Sopenharmony_ci .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 277662306a36Sopenharmony_ci | UVC_QUIRK_PROBE_DEF) }, 277762306a36Sopenharmony_ci /* IMC Networks (Medion Akoya) */ 277862306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 277962306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 278062306a36Sopenharmony_ci .idVendor = 0x13d3, 278162306a36Sopenharmony_ci .idProduct = 0x5103, 278262306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 278362306a36Sopenharmony_ci .bInterfaceSubClass = 1, 278462306a36Sopenharmony_ci .bInterfaceProtocol = 0, 278562306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 278662306a36Sopenharmony_ci /* JMicron USB2.0 XGA WebCam */ 278762306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 278862306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 278962306a36Sopenharmony_ci .idVendor = 0x152d, 279062306a36Sopenharmony_ci .idProduct = 0x0310, 279162306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 279262306a36Sopenharmony_ci .bInterfaceSubClass = 1, 279362306a36Sopenharmony_ci .bInterfaceProtocol = 0, 279462306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 279562306a36Sopenharmony_ci /* Syntek (HP Spartan) */ 279662306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 279762306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 279862306a36Sopenharmony_ci .idVendor = 0x174f, 279962306a36Sopenharmony_ci .idProduct = 0x5212, 280062306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 280162306a36Sopenharmony_ci .bInterfaceSubClass = 1, 280262306a36Sopenharmony_ci .bInterfaceProtocol = 0, 280362306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 280462306a36Sopenharmony_ci /* Syntek (Samsung Q310) */ 280562306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 280662306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 280762306a36Sopenharmony_ci .idVendor = 0x174f, 280862306a36Sopenharmony_ci .idProduct = 0x5931, 280962306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 281062306a36Sopenharmony_ci .bInterfaceSubClass = 1, 281162306a36Sopenharmony_ci .bInterfaceProtocol = 0, 281262306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 281362306a36Sopenharmony_ci /* Syntek (Packard Bell EasyNote MX52 */ 281462306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 281562306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 281662306a36Sopenharmony_ci .idVendor = 0x174f, 281762306a36Sopenharmony_ci .idProduct = 0x8a12, 281862306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 281962306a36Sopenharmony_ci .bInterfaceSubClass = 1, 282062306a36Sopenharmony_ci .bInterfaceProtocol = 0, 282162306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 282262306a36Sopenharmony_ci /* Syntek (Asus F9SG) */ 282362306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 282462306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 282562306a36Sopenharmony_ci .idVendor = 0x174f, 282662306a36Sopenharmony_ci .idProduct = 0x8a31, 282762306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 282862306a36Sopenharmony_ci .bInterfaceSubClass = 1, 282962306a36Sopenharmony_ci .bInterfaceProtocol = 0, 283062306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 283162306a36Sopenharmony_ci /* Syntek (Asus U3S) */ 283262306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 283362306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 283462306a36Sopenharmony_ci .idVendor = 0x174f, 283562306a36Sopenharmony_ci .idProduct = 0x8a33, 283662306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 283762306a36Sopenharmony_ci .bInterfaceSubClass = 1, 283862306a36Sopenharmony_ci .bInterfaceProtocol = 0, 283962306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 284062306a36Sopenharmony_ci /* Syntek (JAOtech Smart Terminal) */ 284162306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 284262306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 284362306a36Sopenharmony_ci .idVendor = 0x174f, 284462306a36Sopenharmony_ci .idProduct = 0x8a34, 284562306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 284662306a36Sopenharmony_ci .bInterfaceSubClass = 1, 284762306a36Sopenharmony_ci .bInterfaceProtocol = 0, 284862306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 284962306a36Sopenharmony_ci /* Miricle 307K */ 285062306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 285162306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 285262306a36Sopenharmony_ci .idVendor = 0x17dc, 285362306a36Sopenharmony_ci .idProduct = 0x0202, 285462306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 285562306a36Sopenharmony_ci .bInterfaceSubClass = 1, 285662306a36Sopenharmony_ci .bInterfaceProtocol = 0, 285762306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 285862306a36Sopenharmony_ci /* Lenovo Thinkpad SL400/SL500 */ 285962306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 286062306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 286162306a36Sopenharmony_ci .idVendor = 0x17ef, 286262306a36Sopenharmony_ci .idProduct = 0x480b, 286362306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 286462306a36Sopenharmony_ci .bInterfaceSubClass = 1, 286562306a36Sopenharmony_ci .bInterfaceProtocol = 0, 286662306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid }, 286762306a36Sopenharmony_ci /* Aveo Technology USB 2.0 Camera */ 286862306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 286962306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 287062306a36Sopenharmony_ci .idVendor = 0x1871, 287162306a36Sopenharmony_ci .idProduct = 0x0306, 287262306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 287362306a36Sopenharmony_ci .bInterfaceSubClass = 1, 287462306a36Sopenharmony_ci .bInterfaceProtocol = 0, 287562306a36Sopenharmony_ci .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 287662306a36Sopenharmony_ci | UVC_QUIRK_PROBE_EXTRAFIELDS) }, 287762306a36Sopenharmony_ci /* Aveo Technology USB 2.0 Camera (Tasco USB Microscope) */ 287862306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 287962306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 288062306a36Sopenharmony_ci .idVendor = 0x1871, 288162306a36Sopenharmony_ci .idProduct = 0x0516, 288262306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 288362306a36Sopenharmony_ci .bInterfaceSubClass = 1, 288462306a36Sopenharmony_ci .bInterfaceProtocol = 0 }, 288562306a36Sopenharmony_ci /* Ecamm Pico iMage */ 288662306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 288762306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 288862306a36Sopenharmony_ci .idVendor = 0x18cd, 288962306a36Sopenharmony_ci .idProduct = 0xcafe, 289062306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 289162306a36Sopenharmony_ci .bInterfaceSubClass = 1, 289262306a36Sopenharmony_ci .bInterfaceProtocol = 0, 289362306a36Sopenharmony_ci .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_EXTRAFIELDS) }, 289462306a36Sopenharmony_ci /* Manta MM-353 Plako */ 289562306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 289662306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 289762306a36Sopenharmony_ci .idVendor = 0x18ec, 289862306a36Sopenharmony_ci .idProduct = 0x3188, 289962306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 290062306a36Sopenharmony_ci .bInterfaceSubClass = 1, 290162306a36Sopenharmony_ci .bInterfaceProtocol = 0, 290262306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 290362306a36Sopenharmony_ci /* FSC WebCam V30S */ 290462306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 290562306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 290662306a36Sopenharmony_ci .idVendor = 0x18ec, 290762306a36Sopenharmony_ci .idProduct = 0x3288, 290862306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 290962306a36Sopenharmony_ci .bInterfaceSubClass = 1, 291062306a36Sopenharmony_ci .bInterfaceProtocol = 0, 291162306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 291262306a36Sopenharmony_ci /* Arkmicro unbranded */ 291362306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 291462306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 291562306a36Sopenharmony_ci .idVendor = 0x18ec, 291662306a36Sopenharmony_ci .idProduct = 0x3290, 291762306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 291862306a36Sopenharmony_ci .bInterfaceSubClass = 1, 291962306a36Sopenharmony_ci .bInterfaceProtocol = 0, 292062306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_def }, 292162306a36Sopenharmony_ci /* The Imaging Source USB CCD cameras */ 292262306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 292362306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 292462306a36Sopenharmony_ci .idVendor = 0x199e, 292562306a36Sopenharmony_ci .idProduct = 0x8102, 292662306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 292762306a36Sopenharmony_ci .bInterfaceSubClass = 1, 292862306a36Sopenharmony_ci .bInterfaceProtocol = 0 }, 292962306a36Sopenharmony_ci /* Bodelin ProScopeHR */ 293062306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 293162306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_DEV_HI 293262306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 293362306a36Sopenharmony_ci .idVendor = 0x19ab, 293462306a36Sopenharmony_ci .idProduct = 0x1000, 293562306a36Sopenharmony_ci .bcdDevice_hi = 0x0126, 293662306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 293762306a36Sopenharmony_ci .bInterfaceSubClass = 1, 293862306a36Sopenharmony_ci .bInterfaceProtocol = 0, 293962306a36Sopenharmony_ci .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_STATUS_INTERVAL) }, 294062306a36Sopenharmony_ci /* MSI StarCam 370i */ 294162306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 294262306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 294362306a36Sopenharmony_ci .idVendor = 0x1b3b, 294462306a36Sopenharmony_ci .idProduct = 0x2951, 294562306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 294662306a36Sopenharmony_ci .bInterfaceSubClass = 1, 294762306a36Sopenharmony_ci .bInterfaceProtocol = 0, 294862306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 294962306a36Sopenharmony_ci /* Generalplus Technology Inc. 808 Camera */ 295062306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 295162306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 295262306a36Sopenharmony_ci .idVendor = 0x1b3f, 295362306a36Sopenharmony_ci .idProduct = 0x2002, 295462306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 295562306a36Sopenharmony_ci .bInterfaceSubClass = 1, 295662306a36Sopenharmony_ci .bInterfaceProtocol = 0, 295762306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, 295862306a36Sopenharmony_ci /* Shenzhen Aoni Electronic Co.,Ltd 2K FHD camera */ 295962306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 296062306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 296162306a36Sopenharmony_ci .idVendor = 0x1bcf, 296262306a36Sopenharmony_ci .idProduct = 0x0b40, 296362306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 296462306a36Sopenharmony_ci .bInterfaceSubClass = 1, 296562306a36Sopenharmony_ci .bInterfaceProtocol = 0, 296662306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&(const struct uvc_device_info){ 296762306a36Sopenharmony_ci .uvc_version = 0x010a, 296862306a36Sopenharmony_ci } }, 296962306a36Sopenharmony_ci /* SiGma Micro USB Web Camera */ 297062306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 297162306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 297262306a36Sopenharmony_ci .idVendor = 0x1c4f, 297362306a36Sopenharmony_ci .idProduct = 0x3000, 297462306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 297562306a36Sopenharmony_ci .bInterfaceSubClass = 1, 297662306a36Sopenharmony_ci .bInterfaceProtocol = 0, 297762306a36Sopenharmony_ci .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_PROBE_MINMAX 297862306a36Sopenharmony_ci | UVC_QUIRK_IGNORE_SELECTOR_UNIT) }, 297962306a36Sopenharmony_ci /* Oculus VR Positional Tracker DK2 */ 298062306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 298162306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 298262306a36Sopenharmony_ci .idVendor = 0x2833, 298362306a36Sopenharmony_ci .idProduct = 0x0201, 298462306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 298562306a36Sopenharmony_ci .bInterfaceSubClass = 1, 298662306a36Sopenharmony_ci .bInterfaceProtocol = 0, 298762306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_force_y8 }, 298862306a36Sopenharmony_ci /* Oculus VR Rift Sensor */ 298962306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 299062306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 299162306a36Sopenharmony_ci .idVendor = 0x2833, 299262306a36Sopenharmony_ci .idProduct = 0x0211, 299362306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 299462306a36Sopenharmony_ci .bInterfaceSubClass = 1, 299562306a36Sopenharmony_ci .bInterfaceProtocol = 0, 299662306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_quirk_force_y8 }, 299762306a36Sopenharmony_ci /* GEO Semiconductor GC6500 */ 299862306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 299962306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 300062306a36Sopenharmony_ci .idVendor = 0x29fe, 300162306a36Sopenharmony_ci .idProduct = 0x4d53, 300262306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 300362306a36Sopenharmony_ci .bInterfaceSubClass = 1, 300462306a36Sopenharmony_ci .bInterfaceProtocol = 0, 300562306a36Sopenharmony_ci .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_FORCE_BPP) }, 300662306a36Sopenharmony_ci /* SunplusIT Inc HD Camera */ 300762306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 300862306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 300962306a36Sopenharmony_ci .idVendor = 0x2b7e, 301062306a36Sopenharmony_ci .idProduct = 0xb752, 301162306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 301262306a36Sopenharmony_ci .bInterfaceSubClass = 1, 301362306a36Sopenharmony_ci .bInterfaceProtocol = UVC_PC_PROTOCOL_15, 301462306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 }, 301562306a36Sopenharmony_ci /* Lenovo Integrated Camera */ 301662306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 301762306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 301862306a36Sopenharmony_ci .idVendor = 0x30c9, 301962306a36Sopenharmony_ci .idProduct = 0x0093, 302062306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 302162306a36Sopenharmony_ci .bInterfaceSubClass = 1, 302262306a36Sopenharmony_ci .bInterfaceProtocol = UVC_PC_PROTOCOL_15, 302362306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_uvc11 }, 302462306a36Sopenharmony_ci /* Sonix Technology USB 2.0 Camera */ 302562306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 302662306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 302762306a36Sopenharmony_ci .idVendor = 0x3277, 302862306a36Sopenharmony_ci .idProduct = 0x0072, 302962306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 303062306a36Sopenharmony_ci .bInterfaceSubClass = 1, 303162306a36Sopenharmony_ci .bInterfaceProtocol = 0, 303262306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 303362306a36Sopenharmony_ci /* Acer EasyCamera */ 303462306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 303562306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 303662306a36Sopenharmony_ci .idVendor = 0x5986, 303762306a36Sopenharmony_ci .idProduct = 0x1172, 303862306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 303962306a36Sopenharmony_ci .bInterfaceSubClass = 1, 304062306a36Sopenharmony_ci .bInterfaceProtocol = 0, 304162306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 304262306a36Sopenharmony_ci /* Acer EasyCamera */ 304362306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 304462306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 304562306a36Sopenharmony_ci .idVendor = 0x5986, 304662306a36Sopenharmony_ci .idProduct = 0x1180, 304762306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 304862306a36Sopenharmony_ci .bInterfaceSubClass = 1, 304962306a36Sopenharmony_ci .bInterfaceProtocol = 0, 305062306a36Sopenharmony_ci .driver_info = (kernel_ulong_t)&uvc_ctrl_power_line_limited }, 305162306a36Sopenharmony_ci /* Intel D410/ASR depth camera */ 305262306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 305362306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 305462306a36Sopenharmony_ci .idVendor = 0x8086, 305562306a36Sopenharmony_ci .idProduct = 0x0ad2, 305662306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 305762306a36Sopenharmony_ci .bInterfaceSubClass = 1, 305862306a36Sopenharmony_ci .bInterfaceProtocol = 0, 305962306a36Sopenharmony_ci .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 306062306a36Sopenharmony_ci /* Intel D415/ASRC depth camera */ 306162306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 306262306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 306362306a36Sopenharmony_ci .idVendor = 0x8086, 306462306a36Sopenharmony_ci .idProduct = 0x0ad3, 306562306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 306662306a36Sopenharmony_ci .bInterfaceSubClass = 1, 306762306a36Sopenharmony_ci .bInterfaceProtocol = 0, 306862306a36Sopenharmony_ci .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 306962306a36Sopenharmony_ci /* Intel D430/AWG depth camera */ 307062306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 307162306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 307262306a36Sopenharmony_ci .idVendor = 0x8086, 307362306a36Sopenharmony_ci .idProduct = 0x0ad4, 307462306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 307562306a36Sopenharmony_ci .bInterfaceSubClass = 1, 307662306a36Sopenharmony_ci .bInterfaceProtocol = 0, 307762306a36Sopenharmony_ci .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 307862306a36Sopenharmony_ci /* Intel RealSense D4M */ 307962306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 308062306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 308162306a36Sopenharmony_ci .idVendor = 0x8086, 308262306a36Sopenharmony_ci .idProduct = 0x0b03, 308362306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 308462306a36Sopenharmony_ci .bInterfaceSubClass = 1, 308562306a36Sopenharmony_ci .bInterfaceProtocol = 0, 308662306a36Sopenharmony_ci .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 308762306a36Sopenharmony_ci /* Intel D435/AWGC depth camera */ 308862306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 308962306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 309062306a36Sopenharmony_ci .idVendor = 0x8086, 309162306a36Sopenharmony_ci .idProduct = 0x0b07, 309262306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 309362306a36Sopenharmony_ci .bInterfaceSubClass = 1, 309462306a36Sopenharmony_ci .bInterfaceProtocol = 0, 309562306a36Sopenharmony_ci .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 309662306a36Sopenharmony_ci /* Intel D435i depth camera */ 309762306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 309862306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 309962306a36Sopenharmony_ci .idVendor = 0x8086, 310062306a36Sopenharmony_ci .idProduct = 0x0b3a, 310162306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 310262306a36Sopenharmony_ci .bInterfaceSubClass = 1, 310362306a36Sopenharmony_ci .bInterfaceProtocol = 0, 310462306a36Sopenharmony_ci .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 310562306a36Sopenharmony_ci /* Intel D405 Depth Camera */ 310662306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 310762306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 310862306a36Sopenharmony_ci .idVendor = 0x8086, 310962306a36Sopenharmony_ci .idProduct = 0x0b5b, 311062306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 311162306a36Sopenharmony_ci .bInterfaceSubClass = 1, 311262306a36Sopenharmony_ci .bInterfaceProtocol = 0, 311362306a36Sopenharmony_ci .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 311462306a36Sopenharmony_ci /* Intel D455 Depth Camera */ 311562306a36Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE 311662306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_INFO, 311762306a36Sopenharmony_ci .idVendor = 0x8086, 311862306a36Sopenharmony_ci .idProduct = 0x0b5c, 311962306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_VIDEO, 312062306a36Sopenharmony_ci .bInterfaceSubClass = 1, 312162306a36Sopenharmony_ci .bInterfaceProtocol = 0, 312262306a36Sopenharmony_ci .driver_info = UVC_INFO_META(V4L2_META_FMT_D4XX) }, 312362306a36Sopenharmony_ci /* Generic USB Video Class */ 312462306a36Sopenharmony_ci { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_UNDEFINED) }, 312562306a36Sopenharmony_ci { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_15) }, 312662306a36Sopenharmony_ci {} 312762306a36Sopenharmony_ci}; 312862306a36Sopenharmony_ci 312962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, uvc_ids); 313062306a36Sopenharmony_ci 313162306a36Sopenharmony_cistruct uvc_driver uvc_driver = { 313262306a36Sopenharmony_ci .driver = { 313362306a36Sopenharmony_ci .name = "uvcvideo", 313462306a36Sopenharmony_ci .probe = uvc_probe, 313562306a36Sopenharmony_ci .disconnect = uvc_disconnect, 313662306a36Sopenharmony_ci .suspend = uvc_suspend, 313762306a36Sopenharmony_ci .resume = uvc_resume, 313862306a36Sopenharmony_ci .reset_resume = uvc_reset_resume, 313962306a36Sopenharmony_ci .id_table = uvc_ids, 314062306a36Sopenharmony_ci .supports_autosuspend = 1, 314162306a36Sopenharmony_ci }, 314262306a36Sopenharmony_ci}; 314362306a36Sopenharmony_ci 314462306a36Sopenharmony_cistatic int __init uvc_init(void) 314562306a36Sopenharmony_ci{ 314662306a36Sopenharmony_ci int ret; 314762306a36Sopenharmony_ci 314862306a36Sopenharmony_ci uvc_debugfs_init(); 314962306a36Sopenharmony_ci 315062306a36Sopenharmony_ci ret = usb_register(&uvc_driver.driver); 315162306a36Sopenharmony_ci if (ret < 0) { 315262306a36Sopenharmony_ci uvc_debugfs_cleanup(); 315362306a36Sopenharmony_ci return ret; 315462306a36Sopenharmony_ci } 315562306a36Sopenharmony_ci 315662306a36Sopenharmony_ci return 0; 315762306a36Sopenharmony_ci} 315862306a36Sopenharmony_ci 315962306a36Sopenharmony_cistatic void __exit uvc_cleanup(void) 316062306a36Sopenharmony_ci{ 316162306a36Sopenharmony_ci usb_deregister(&uvc_driver.driver); 316262306a36Sopenharmony_ci uvc_debugfs_cleanup(); 316362306a36Sopenharmony_ci} 316462306a36Sopenharmony_ci 316562306a36Sopenharmony_cimodule_init(uvc_init); 316662306a36Sopenharmony_cimodule_exit(uvc_cleanup); 316762306a36Sopenharmony_ci 316862306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 316962306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 317062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 317162306a36Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION); 317262306a36Sopenharmony_ci 3173