12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab * uvc_v4l2.c -- USB Video Class driver - V4L2 API
40c0d06caSMauro Carvalho Chehab *
50c0d06caSMauro Carvalho Chehab * Copyright (C) 2005-2010
60c0d06caSMauro Carvalho Chehab * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
70c0d06caSMauro Carvalho Chehab */
80c0d06caSMauro Carvalho Chehab
940140edaSRicardo Ribalda #include <linux/bits.h>
100c0d06caSMauro Carvalho Chehab #include <linux/compat.h>
110c0d06caSMauro Carvalho Chehab #include <linux/kernel.h>
120c0d06caSMauro Carvalho Chehab #include <linux/list.h>
130c0d06caSMauro Carvalho Chehab #include <linux/module.h>
140c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
150c0d06caSMauro Carvalho Chehab #include <linux/usb.h>
160c0d06caSMauro Carvalho Chehab #include <linux/videodev2.h>
170c0d06caSMauro Carvalho Chehab #include <linux/vmalloc.h>
180c0d06caSMauro Carvalho Chehab #include <linux/mm.h>
190c0d06caSMauro Carvalho Chehab #include <linux/wait.h>
200c0d06caSMauro Carvalho Chehab #include <linux/atomic.h>
210c0d06caSMauro Carvalho Chehab
220c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h>
230c0d06caSMauro Carvalho Chehab #include <media/v4l2-ctrls.h>
240c0d06caSMauro Carvalho Chehab #include <media/v4l2-event.h>
250c0d06caSMauro Carvalho Chehab #include <media/v4l2-ioctl.h>
260c0d06caSMauro Carvalho Chehab
270c0d06caSMauro Carvalho Chehab #include "uvcvideo.h"
280c0d06caSMauro Carvalho Chehab
uvc_control_add_xu_mapping(struct uvc_video_chain * chain,struct uvc_control_mapping * map,const struct uvc_xu_control_mapping * xmap)29716c3304SRicardo Ribalda static int uvc_control_add_xu_mapping(struct uvc_video_chain *chain,
30716c3304SRicardo Ribalda struct uvc_control_mapping *map,
31716c3304SRicardo Ribalda const struct uvc_xu_control_mapping *xmap)
32716c3304SRicardo Ribalda {
33716c3304SRicardo Ribalda unsigned int i;
34716c3304SRicardo Ribalda size_t size;
35716c3304SRicardo Ribalda int ret;
36716c3304SRicardo Ribalda
37716c3304SRicardo Ribalda /*
38716c3304SRicardo Ribalda * Prevent excessive memory consumption, as well as integer
39716c3304SRicardo Ribalda * overflows.
40716c3304SRicardo Ribalda */
41716c3304SRicardo Ribalda if (xmap->menu_count == 0 ||
42716c3304SRicardo Ribalda xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES)
43716c3304SRicardo Ribalda return -EINVAL;
44716c3304SRicardo Ribalda
45716c3304SRicardo Ribalda map->menu_names = NULL;
46716c3304SRicardo Ribalda map->menu_mapping = NULL;
47716c3304SRicardo Ribalda
486d00f4ecSLaurent Pinchart map->menu_mask = GENMASK(xmap->menu_count - 1, 0);
49716c3304SRicardo Ribalda
50716c3304SRicardo Ribalda size = xmap->menu_count * sizeof(*map->menu_mapping);
51716c3304SRicardo Ribalda map->menu_mapping = kzalloc(size, GFP_KERNEL);
52716c3304SRicardo Ribalda if (!map->menu_mapping) {
53716c3304SRicardo Ribalda ret = -ENOMEM;
54716c3304SRicardo Ribalda goto done;
55716c3304SRicardo Ribalda }
56716c3304SRicardo Ribalda
57716c3304SRicardo Ribalda for (i = 0; i < xmap->menu_count ; i++) {
58716c3304SRicardo Ribalda if (copy_from_user((u32 *)&map->menu_mapping[i],
59716c3304SRicardo Ribalda &xmap->menu_info[i].value,
60716c3304SRicardo Ribalda sizeof(map->menu_mapping[i]))) {
61716c3304SRicardo Ribalda ret = -EACCES;
62716c3304SRicardo Ribalda goto done;
63716c3304SRicardo Ribalda }
64716c3304SRicardo Ribalda }
65716c3304SRicardo Ribalda
66716c3304SRicardo Ribalda /*
67716c3304SRicardo Ribalda * Always use the standard naming if available, otherwise copy the
68716c3304SRicardo Ribalda * names supplied by userspace.
69716c3304SRicardo Ribalda */
70716c3304SRicardo Ribalda if (!v4l2_ctrl_get_menu(map->id)) {
71716c3304SRicardo Ribalda size = xmap->menu_count * sizeof(map->menu_names[0]);
72716c3304SRicardo Ribalda map->menu_names = kzalloc(size, GFP_KERNEL);
73716c3304SRicardo Ribalda if (!map->menu_names) {
74716c3304SRicardo Ribalda ret = -ENOMEM;
75716c3304SRicardo Ribalda goto done;
76716c3304SRicardo Ribalda }
77716c3304SRicardo Ribalda
78716c3304SRicardo Ribalda for (i = 0; i < xmap->menu_count ; i++) {
79716c3304SRicardo Ribalda /* sizeof(names[i]) - 1: to take care of \0 */
80716c3304SRicardo Ribalda if (copy_from_user((char *)map->menu_names[i],
81716c3304SRicardo Ribalda xmap->menu_info[i].name,
82716c3304SRicardo Ribalda sizeof(map->menu_names[i]) - 1)) {
83716c3304SRicardo Ribalda ret = -EACCES;
84716c3304SRicardo Ribalda goto done;
85716c3304SRicardo Ribalda }
86716c3304SRicardo Ribalda }
87716c3304SRicardo Ribalda }
88716c3304SRicardo Ribalda
89716c3304SRicardo Ribalda ret = uvc_ctrl_add_mapping(chain, map);
90716c3304SRicardo Ribalda
91716c3304SRicardo Ribalda done:
92716c3304SRicardo Ribalda kfree(map->menu_names);
93716c3304SRicardo Ribalda map->menu_names = NULL;
94716c3304SRicardo Ribalda kfree(map->menu_mapping);
95716c3304SRicardo Ribalda map->menu_mapping = NULL;
96716c3304SRicardo Ribalda
97716c3304SRicardo Ribalda return ret;
98716c3304SRicardo Ribalda }
99716c3304SRicardo Ribalda
1000c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
1010c0d06caSMauro Carvalho Chehab * UVC ioctls
1020c0d06caSMauro Carvalho Chehab */
uvc_ioctl_xu_ctrl_map(struct uvc_video_chain * chain,struct uvc_xu_control_mapping * xmap)103716c3304SRicardo Ribalda static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain,
1040c0d06caSMauro Carvalho Chehab struct uvc_xu_control_mapping *xmap)
1050c0d06caSMauro Carvalho Chehab {
1060c0d06caSMauro Carvalho Chehab struct uvc_control_mapping *map;
1070c0d06caSMauro Carvalho Chehab int ret;
1080c0d06caSMauro Carvalho Chehab
109f14d4988SLaurent Pinchart map = kzalloc(sizeof(*map), GFP_KERNEL);
1100c0d06caSMauro Carvalho Chehab if (map == NULL)
1110c0d06caSMauro Carvalho Chehab return -ENOMEM;
1120c0d06caSMauro Carvalho Chehab
1130c0d06caSMauro Carvalho Chehab map->id = xmap->id;
11470fa906dSRicardo Ribalda /* Non standard control id. */
11570fa906dSRicardo Ribalda if (v4l2_ctrl_get_name(map->id) == NULL) {
11618a9b21fSRicardo Ribalda if (xmap->name[0] == '\0') {
11718a9b21fSRicardo Ribalda ret = -EINVAL;
1184b065060SColin Ian King goto free_map;
1194b065060SColin Ian King }
12018a9b21fSRicardo Ribalda xmap->name[sizeof(xmap->name) - 1] = '\0';
12118a9b21fSRicardo Ribalda map->name = xmap->name;
12270fa906dSRicardo Ribalda }
123f14d4988SLaurent Pinchart memcpy(map->entity, xmap->entity, sizeof(map->entity));
1240c0d06caSMauro Carvalho Chehab map->selector = xmap->selector;
1250c0d06caSMauro Carvalho Chehab map->size = xmap->size;
1260c0d06caSMauro Carvalho Chehab map->offset = xmap->offset;
1270c0d06caSMauro Carvalho Chehab map->v4l2_type = xmap->v4l2_type;
1280c0d06caSMauro Carvalho Chehab map->data_type = xmap->data_type;
1290c0d06caSMauro Carvalho Chehab
1300c0d06caSMauro Carvalho Chehab switch (xmap->v4l2_type) {
1310c0d06caSMauro Carvalho Chehab case V4L2_CTRL_TYPE_INTEGER:
1320c0d06caSMauro Carvalho Chehab case V4L2_CTRL_TYPE_BOOLEAN:
1330c0d06caSMauro Carvalho Chehab case V4L2_CTRL_TYPE_BUTTON:
134716c3304SRicardo Ribalda ret = uvc_ctrl_add_mapping(chain, map);
1350c0d06caSMauro Carvalho Chehab break;
1360c0d06caSMauro Carvalho Chehab
1370c0d06caSMauro Carvalho Chehab case V4L2_CTRL_TYPE_MENU:
138716c3304SRicardo Ribalda ret = uvc_control_add_xu_mapping(chain, map, xmap);
1390c0d06caSMauro Carvalho Chehab break;
1400c0d06caSMauro Carvalho Chehab
1410c0d06caSMauro Carvalho Chehab default:
1429e56380aSJoe Perches uvc_dbg(chain->dev, CONTROL,
1439e56380aSJoe Perches "Unsupported V4L2 control type %u\n", xmap->v4l2_type);
1440c0d06caSMauro Carvalho Chehab ret = -ENOTTY;
145716c3304SRicardo Ribalda break;
1460c0d06caSMauro Carvalho Chehab }
1470c0d06caSMauro Carvalho Chehab
148cf2113caSDan Carpenter free_map:
1490c0d06caSMauro Carvalho Chehab kfree(map);
1500c0d06caSMauro Carvalho Chehab
1510c0d06caSMauro Carvalho Chehab return ret;
1520c0d06caSMauro Carvalho Chehab }
1530c0d06caSMauro Carvalho Chehab
1540c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
1550c0d06caSMauro Carvalho Chehab * V4L2 interface
1560c0d06caSMauro Carvalho Chehab */
1570c0d06caSMauro Carvalho Chehab
1580c0d06caSMauro Carvalho Chehab /*
1590c0d06caSMauro Carvalho Chehab * Find the frame interval closest to the requested frame interval for the
1600c0d06caSMauro Carvalho Chehab * given frame format and size. This should be done by the device as part of
1610c0d06caSMauro Carvalho Chehab * the Video Probe and Commit negotiation, but some hardware don't implement
1620c0d06caSMauro Carvalho Chehab * that feature.
1630c0d06caSMauro Carvalho Chehab */
uvc_try_frame_interval(const struct uvc_frame * frame,u32 interval)164af621ba2SLaurent Pinchart static u32 uvc_try_frame_interval(const struct uvc_frame *frame, u32 interval)
1650c0d06caSMauro Carvalho Chehab {
1660c0d06caSMauro Carvalho Chehab unsigned int i;
1670c0d06caSMauro Carvalho Chehab
1680c0d06caSMauro Carvalho Chehab if (frame->bFrameIntervalType) {
1692c6b222cSLaurent Pinchart u32 best = -1, dist;
1700c0d06caSMauro Carvalho Chehab
1710c0d06caSMauro Carvalho Chehab for (i = 0; i < frame->bFrameIntervalType; ++i) {
1720c0d06caSMauro Carvalho Chehab dist = interval > frame->dwFrameInterval[i]
1730c0d06caSMauro Carvalho Chehab ? interval - frame->dwFrameInterval[i]
1740c0d06caSMauro Carvalho Chehab : frame->dwFrameInterval[i] - interval;
1750c0d06caSMauro Carvalho Chehab
1760c0d06caSMauro Carvalho Chehab if (dist > best)
1770c0d06caSMauro Carvalho Chehab break;
1780c0d06caSMauro Carvalho Chehab
1790c0d06caSMauro Carvalho Chehab best = dist;
1800c0d06caSMauro Carvalho Chehab }
1810c0d06caSMauro Carvalho Chehab
1820c0d06caSMauro Carvalho Chehab interval = frame->dwFrameInterval[i-1];
1830c0d06caSMauro Carvalho Chehab } else {
1842c6b222cSLaurent Pinchart const u32 min = frame->dwFrameInterval[0];
1852c6b222cSLaurent Pinchart const u32 max = frame->dwFrameInterval[1];
1862c6b222cSLaurent Pinchart const u32 step = frame->dwFrameInterval[2];
1870c0d06caSMauro Carvalho Chehab
1880c0d06caSMauro Carvalho Chehab interval = min + (interval - min + step/2) / step * step;
1890c0d06caSMauro Carvalho Chehab if (interval > max)
1900c0d06caSMauro Carvalho Chehab interval = max;
1910c0d06caSMauro Carvalho Chehab }
1920c0d06caSMauro Carvalho Chehab
1930c0d06caSMauro Carvalho Chehab return interval;
1940c0d06caSMauro Carvalho Chehab }
1950c0d06caSMauro Carvalho Chehab
uvc_v4l2_get_bytesperline(const struct uvc_format * format,const struct uvc_frame * frame)1962c6b222cSLaurent Pinchart static u32 uvc_v4l2_get_bytesperline(const struct uvc_format *format,
197f222467aSNicolas Dufresne const struct uvc_frame *frame)
198f222467aSNicolas Dufresne {
199f222467aSNicolas Dufresne switch (format->fcc) {
200f222467aSNicolas Dufresne case V4L2_PIX_FMT_NV12:
201f222467aSNicolas Dufresne case V4L2_PIX_FMT_YVU420:
202f222467aSNicolas Dufresne case V4L2_PIX_FMT_YUV420:
203f222467aSNicolas Dufresne case V4L2_PIX_FMT_M420:
204f222467aSNicolas Dufresne return frame->wWidth;
205f222467aSNicolas Dufresne
206f222467aSNicolas Dufresne default:
207f222467aSNicolas Dufresne return format->bpp * frame->wWidth / 8;
208f222467aSNicolas Dufresne }
209f222467aSNicolas Dufresne }
210f222467aSNicolas Dufresne
uvc_v4l2_try_format(struct uvc_streaming * stream,struct v4l2_format * fmt,struct uvc_streaming_control * probe,const struct uvc_format ** uvc_format,const struct uvc_frame ** uvc_frame)2110c0d06caSMauro Carvalho Chehab static int uvc_v4l2_try_format(struct uvc_streaming *stream,
2120c0d06caSMauro Carvalho Chehab struct v4l2_format *fmt, struct uvc_streaming_control *probe,
213af621ba2SLaurent Pinchart const struct uvc_format **uvc_format,
214af621ba2SLaurent Pinchart const struct uvc_frame **uvc_frame)
2150c0d06caSMauro Carvalho Chehab {
216af621ba2SLaurent Pinchart const struct uvc_format *format = NULL;
217af621ba2SLaurent Pinchart const struct uvc_frame *frame = NULL;
2182c6b222cSLaurent Pinchart u16 rw, rh;
2190c0d06caSMauro Carvalho Chehab unsigned int d, maxd;
2200c0d06caSMauro Carvalho Chehab unsigned int i;
2212c6b222cSLaurent Pinchart u32 interval;
2220c0d06caSMauro Carvalho Chehab int ret = 0;
2232c6b222cSLaurent Pinchart u8 *fcc;
2240c0d06caSMauro Carvalho Chehab
2250c0d06caSMauro Carvalho Chehab if (fmt->type != stream->type)
2260c0d06caSMauro Carvalho Chehab return -EINVAL;
2270c0d06caSMauro Carvalho Chehab
2282c6b222cSLaurent Pinchart fcc = (u8 *)&fmt->fmt.pix.pixelformat;
2299e56380aSJoe Perches uvc_dbg(stream->dev, FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u\n",
2300c0d06caSMauro Carvalho Chehab fmt->fmt.pix.pixelformat,
2310c0d06caSMauro Carvalho Chehab fcc[0], fcc[1], fcc[2], fcc[3],
2320c0d06caSMauro Carvalho Chehab fmt->fmt.pix.width, fmt->fmt.pix.height);
2330c0d06caSMauro Carvalho Chehab
234699b9a86SLaurent Pinchart /*
235699b9a86SLaurent Pinchart * Check if the hardware supports the requested format, use the default
236815adc46SLaurent Pinchart * format otherwise.
237815adc46SLaurent Pinchart */
2380c0d06caSMauro Carvalho Chehab for (i = 0; i < stream->nformats; ++i) {
239ccfad4e8SLaurent Pinchart format = &stream->formats[i];
2400c0d06caSMauro Carvalho Chehab if (format->fcc == fmt->fmt.pix.pixelformat)
2410c0d06caSMauro Carvalho Chehab break;
2420c0d06caSMauro Carvalho Chehab }
2430c0d06caSMauro Carvalho Chehab
244815adc46SLaurent Pinchart if (i == stream->nformats) {
245815adc46SLaurent Pinchart format = stream->def_format;
246815adc46SLaurent Pinchart fmt->fmt.pix.pixelformat = format->fcc;
2470c0d06caSMauro Carvalho Chehab }
2480c0d06caSMauro Carvalho Chehab
249699b9a86SLaurent Pinchart /*
250699b9a86SLaurent Pinchart * Find the closest image size. The distance between image sizes is
2510c0d06caSMauro Carvalho Chehab * the size in pixels of the non-overlapping regions between the
2520c0d06caSMauro Carvalho Chehab * requested size and the frame-specified size.
2530c0d06caSMauro Carvalho Chehab */
2540c0d06caSMauro Carvalho Chehab rw = fmt->fmt.pix.width;
2550c0d06caSMauro Carvalho Chehab rh = fmt->fmt.pix.height;
2560c0d06caSMauro Carvalho Chehab maxd = (unsigned int)-1;
2570c0d06caSMauro Carvalho Chehab
2580c0d06caSMauro Carvalho Chehab for (i = 0; i < format->nframes; ++i) {
259aa8db3adSLaurent Pinchart u16 w = format->frames[i].wWidth;
260aa8db3adSLaurent Pinchart u16 h = format->frames[i].wHeight;
2610c0d06caSMauro Carvalho Chehab
2620c0d06caSMauro Carvalho Chehab d = min(w, rw) * min(h, rh);
2630c0d06caSMauro Carvalho Chehab d = w*h + rw*rh - 2*d;
2640c0d06caSMauro Carvalho Chehab if (d < maxd) {
2650c0d06caSMauro Carvalho Chehab maxd = d;
266aa8db3adSLaurent Pinchart frame = &format->frames[i];
2670c0d06caSMauro Carvalho Chehab }
2680c0d06caSMauro Carvalho Chehab
2690c0d06caSMauro Carvalho Chehab if (maxd == 0)
2700c0d06caSMauro Carvalho Chehab break;
2710c0d06caSMauro Carvalho Chehab }
2720c0d06caSMauro Carvalho Chehab
2730c0d06caSMauro Carvalho Chehab if (frame == NULL) {
2749e56380aSJoe Perches uvc_dbg(stream->dev, FORMAT, "Unsupported size %ux%u\n",
2759e56380aSJoe Perches fmt->fmt.pix.width, fmt->fmt.pix.height);
2760c0d06caSMauro Carvalho Chehab return -EINVAL;
2770c0d06caSMauro Carvalho Chehab }
2780c0d06caSMauro Carvalho Chehab
2790c0d06caSMauro Carvalho Chehab /* Use the default frame interval. */
2800c0d06caSMauro Carvalho Chehab interval = frame->dwDefaultFrameInterval;
2819e56380aSJoe Perches uvc_dbg(stream->dev, FORMAT,
2829e56380aSJoe Perches "Using default frame interval %u.%u us (%u.%u fps)\n",
283ed4c5fa4SRicardo Ribalda interval / 10, interval % 10, 10000000 / interval,
2840c0d06caSMauro Carvalho Chehab (100000000 / interval) % 10);
2850c0d06caSMauro Carvalho Chehab
2860c0d06caSMauro Carvalho Chehab /* Set the format index, frame index and frame interval. */
287f14d4988SLaurent Pinchart memset(probe, 0, sizeof(*probe));
2880c0d06caSMauro Carvalho Chehab probe->bmHint = 1; /* dwFrameInterval */
2890c0d06caSMauro Carvalho Chehab probe->bFormatIndex = format->index;
2900c0d06caSMauro Carvalho Chehab probe->bFrameIndex = frame->bFrameIndex;
2910c0d06caSMauro Carvalho Chehab probe->dwFrameInterval = uvc_try_frame_interval(frame, interval);
292699b9a86SLaurent Pinchart /*
293699b9a86SLaurent Pinchart * Some webcams stall the probe control set request when the
2940c0d06caSMauro Carvalho Chehab * dwMaxVideoFrameSize field is set to zero. The UVC specification
2950c0d06caSMauro Carvalho Chehab * clearly states that the field is read-only from the host, so this
2960c0d06caSMauro Carvalho Chehab * is a webcam bug. Set dwMaxVideoFrameSize to the value reported by
2970c0d06caSMauro Carvalho Chehab * the webcam to work around the problem.
2980c0d06caSMauro Carvalho Chehab *
2990c0d06caSMauro Carvalho Chehab * The workaround could probably be enabled for all webcams, so the
3000c0d06caSMauro Carvalho Chehab * quirk can be removed if needed. It's currently useful to detect
3010c0d06caSMauro Carvalho Chehab * webcam bugs and fix them before they hit the market (providing
3020c0d06caSMauro Carvalho Chehab * developers test their webcams with the Linux driver as well as with
3030c0d06caSMauro Carvalho Chehab * the Windows driver).
3040c0d06caSMauro Carvalho Chehab */
3050c0d06caSMauro Carvalho Chehab mutex_lock(&stream->mutex);
3060c0d06caSMauro Carvalho Chehab if (stream->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS)
3070c0d06caSMauro Carvalho Chehab probe->dwMaxVideoFrameSize =
3080c0d06caSMauro Carvalho Chehab stream->ctrl.dwMaxVideoFrameSize;
3090c0d06caSMauro Carvalho Chehab
3100c0d06caSMauro Carvalho Chehab /* Probe the device. */
3110c0d06caSMauro Carvalho Chehab ret = uvc_probe_video(stream, probe);
3120c0d06caSMauro Carvalho Chehab mutex_unlock(&stream->mutex);
3130c0d06caSMauro Carvalho Chehab if (ret < 0)
3141fef11a2SMichael Grzeschik return ret;
3150c0d06caSMauro Carvalho Chehab
316699b9a86SLaurent Pinchart /*
317699b9a86SLaurent Pinchart * After the probe, update fmt with the values returned from
318dc9455ffSLaurent Pinchart * negotiation with the device. Some devices return invalid bFormatIndex
319dc9455ffSLaurent Pinchart * and bFrameIndex values, in which case we can only assume they have
320dc9455ffSLaurent Pinchart * accepted the requested format as-is.
3218a652a17SAdam Goode */
3228a652a17SAdam Goode for (i = 0; i < stream->nformats; ++i) {
323ccfad4e8SLaurent Pinchart if (probe->bFormatIndex == stream->formats[i].index) {
324ccfad4e8SLaurent Pinchart format = &stream->formats[i];
3258a652a17SAdam Goode break;
3268a652a17SAdam Goode }
3278a652a17SAdam Goode }
3288a652a17SAdam Goode
329dc9455ffSLaurent Pinchart if (i == stream->nformats)
3309e56380aSJoe Perches uvc_dbg(stream->dev, FORMAT,
331dc9455ffSLaurent Pinchart "Unknown bFormatIndex %u, using default\n",
3328a652a17SAdam Goode probe->bFormatIndex);
3338a652a17SAdam Goode
3348a652a17SAdam Goode for (i = 0; i < format->nframes; ++i) {
335aa8db3adSLaurent Pinchart if (probe->bFrameIndex == format->frames[i].bFrameIndex) {
336aa8db3adSLaurent Pinchart frame = &format->frames[i];
3378a652a17SAdam Goode break;
3388a652a17SAdam Goode }
3398a652a17SAdam Goode }
3408a652a17SAdam Goode
341dc9455ffSLaurent Pinchart if (i == format->nframes)
3429e56380aSJoe Perches uvc_dbg(stream->dev, FORMAT,
343dc9455ffSLaurent Pinchart "Unknown bFrameIndex %u, using default\n",
3448a652a17SAdam Goode probe->bFrameIndex);
3458a652a17SAdam Goode
3460c0d06caSMauro Carvalho Chehab fmt->fmt.pix.width = frame->wWidth;
3470c0d06caSMauro Carvalho Chehab fmt->fmt.pix.height = frame->wHeight;
3480c0d06caSMauro Carvalho Chehab fmt->fmt.pix.field = V4L2_FIELD_NONE;
349f222467aSNicolas Dufresne fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame);
3500c0d06caSMauro Carvalho Chehab fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize;
3518a652a17SAdam Goode fmt->fmt.pix.pixelformat = format->fcc;
3520c0d06caSMauro Carvalho Chehab fmt->fmt.pix.colorspace = format->colorspace;
353ec2c23f6SAdam Goode fmt->fmt.pix.xfer_func = format->xfer_func;
354ec2c23f6SAdam Goode fmt->fmt.pix.ycbcr_enc = format->ycbcr_enc;
3550c0d06caSMauro Carvalho Chehab
3560c0d06caSMauro Carvalho Chehab if (uvc_format != NULL)
3570c0d06caSMauro Carvalho Chehab *uvc_format = format;
3580c0d06caSMauro Carvalho Chehab if (uvc_frame != NULL)
3590c0d06caSMauro Carvalho Chehab *uvc_frame = frame;
3600c0d06caSMauro Carvalho Chehab
3610c0d06caSMauro Carvalho Chehab return ret;
3620c0d06caSMauro Carvalho Chehab }
3630c0d06caSMauro Carvalho Chehab
uvc_v4l2_get_format(struct uvc_streaming * stream,struct v4l2_format * fmt)3640c0d06caSMauro Carvalho Chehab static int uvc_v4l2_get_format(struct uvc_streaming *stream,
3650c0d06caSMauro Carvalho Chehab struct v4l2_format *fmt)
3660c0d06caSMauro Carvalho Chehab {
367af621ba2SLaurent Pinchart const struct uvc_format *format;
368af621ba2SLaurent Pinchart const struct uvc_frame *frame;
3690c0d06caSMauro Carvalho Chehab int ret = 0;
3700c0d06caSMauro Carvalho Chehab
3710c0d06caSMauro Carvalho Chehab if (fmt->type != stream->type)
3720c0d06caSMauro Carvalho Chehab return -EINVAL;
3730c0d06caSMauro Carvalho Chehab
3740c0d06caSMauro Carvalho Chehab mutex_lock(&stream->mutex);
3750c0d06caSMauro Carvalho Chehab format = stream->cur_format;
3760c0d06caSMauro Carvalho Chehab frame = stream->cur_frame;
3770c0d06caSMauro Carvalho Chehab
3780c0d06caSMauro Carvalho Chehab if (format == NULL || frame == NULL) {
3790c0d06caSMauro Carvalho Chehab ret = -EINVAL;
3800c0d06caSMauro Carvalho Chehab goto done;
3810c0d06caSMauro Carvalho Chehab }
3820c0d06caSMauro Carvalho Chehab
3830c0d06caSMauro Carvalho Chehab fmt->fmt.pix.pixelformat = format->fcc;
3840c0d06caSMauro Carvalho Chehab fmt->fmt.pix.width = frame->wWidth;
3850c0d06caSMauro Carvalho Chehab fmt->fmt.pix.height = frame->wHeight;
3860c0d06caSMauro Carvalho Chehab fmt->fmt.pix.field = V4L2_FIELD_NONE;
387f222467aSNicolas Dufresne fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame);
3880c0d06caSMauro Carvalho Chehab fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize;
3890c0d06caSMauro Carvalho Chehab fmt->fmt.pix.colorspace = format->colorspace;
390ec2c23f6SAdam Goode fmt->fmt.pix.xfer_func = format->xfer_func;
391ec2c23f6SAdam Goode fmt->fmt.pix.ycbcr_enc = format->ycbcr_enc;
3920c0d06caSMauro Carvalho Chehab
3930c0d06caSMauro Carvalho Chehab done:
3940c0d06caSMauro Carvalho Chehab mutex_unlock(&stream->mutex);
3950c0d06caSMauro Carvalho Chehab return ret;
3960c0d06caSMauro Carvalho Chehab }
3970c0d06caSMauro Carvalho Chehab
uvc_v4l2_set_format(struct uvc_streaming * stream,struct v4l2_format * fmt)3980c0d06caSMauro Carvalho Chehab static int uvc_v4l2_set_format(struct uvc_streaming *stream,
3990c0d06caSMauro Carvalho Chehab struct v4l2_format *fmt)
4000c0d06caSMauro Carvalho Chehab {
4010c0d06caSMauro Carvalho Chehab struct uvc_streaming_control probe;
402af621ba2SLaurent Pinchart const struct uvc_format *format;
403af621ba2SLaurent Pinchart const struct uvc_frame *frame;
4040c0d06caSMauro Carvalho Chehab int ret;
4050c0d06caSMauro Carvalho Chehab
4060c0d06caSMauro Carvalho Chehab if (fmt->type != stream->type)
4070c0d06caSMauro Carvalho Chehab return -EINVAL;
4080c0d06caSMauro Carvalho Chehab
4090c0d06caSMauro Carvalho Chehab ret = uvc_v4l2_try_format(stream, fmt, &probe, &format, &frame);
4100c0d06caSMauro Carvalho Chehab if (ret < 0)
4110c0d06caSMauro Carvalho Chehab return ret;
4120c0d06caSMauro Carvalho Chehab
4130c0d06caSMauro Carvalho Chehab mutex_lock(&stream->mutex);
4140c0d06caSMauro Carvalho Chehab
4150c0d06caSMauro Carvalho Chehab if (uvc_queue_allocated(&stream->queue)) {
4160c0d06caSMauro Carvalho Chehab ret = -EBUSY;
4170c0d06caSMauro Carvalho Chehab goto done;
4180c0d06caSMauro Carvalho Chehab }
4190c0d06caSMauro Carvalho Chehab
4208c0d44e2SEzequiel Garcia stream->ctrl = probe;
4210c0d06caSMauro Carvalho Chehab stream->cur_format = format;
4220c0d06caSMauro Carvalho Chehab stream->cur_frame = frame;
4230c0d06caSMauro Carvalho Chehab
4240c0d06caSMauro Carvalho Chehab done:
4250c0d06caSMauro Carvalho Chehab mutex_unlock(&stream->mutex);
4260c0d06caSMauro Carvalho Chehab return ret;
4270c0d06caSMauro Carvalho Chehab }
4280c0d06caSMauro Carvalho Chehab
uvc_v4l2_get_streamparm(struct uvc_streaming * stream,struct v4l2_streamparm * parm)4290c0d06caSMauro Carvalho Chehab static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream,
4300c0d06caSMauro Carvalho Chehab struct v4l2_streamparm *parm)
4310c0d06caSMauro Carvalho Chehab {
4321e304c47SLaurent Pinchart u32 numerator, denominator;
4330c0d06caSMauro Carvalho Chehab
4340c0d06caSMauro Carvalho Chehab if (parm->type != stream->type)
4350c0d06caSMauro Carvalho Chehab return -EINVAL;
4360c0d06caSMauro Carvalho Chehab
4370c0d06caSMauro Carvalho Chehab mutex_lock(&stream->mutex);
4380c0d06caSMauro Carvalho Chehab numerator = stream->ctrl.dwFrameInterval;
4390c0d06caSMauro Carvalho Chehab mutex_unlock(&stream->mutex);
4400c0d06caSMauro Carvalho Chehab
4410c0d06caSMauro Carvalho Chehab denominator = 10000000;
4426ba8b8d4SMichael Grzeschik v4l2_simplify_fraction(&numerator, &denominator, 8, 333);
4430c0d06caSMauro Carvalho Chehab
444f14d4988SLaurent Pinchart memset(parm, 0, sizeof(*parm));
4450c0d06caSMauro Carvalho Chehab parm->type = stream->type;
4460c0d06caSMauro Carvalho Chehab
4470c0d06caSMauro Carvalho Chehab if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
4480c0d06caSMauro Carvalho Chehab parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
4490c0d06caSMauro Carvalho Chehab parm->parm.capture.capturemode = 0;
4500c0d06caSMauro Carvalho Chehab parm->parm.capture.timeperframe.numerator = numerator;
4510c0d06caSMauro Carvalho Chehab parm->parm.capture.timeperframe.denominator = denominator;
4520c0d06caSMauro Carvalho Chehab parm->parm.capture.extendedmode = 0;
4530c0d06caSMauro Carvalho Chehab parm->parm.capture.readbuffers = 0;
4540c0d06caSMauro Carvalho Chehab } else {
4550c0d06caSMauro Carvalho Chehab parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
4560c0d06caSMauro Carvalho Chehab parm->parm.output.outputmode = 0;
4570c0d06caSMauro Carvalho Chehab parm->parm.output.timeperframe.numerator = numerator;
4580c0d06caSMauro Carvalho Chehab parm->parm.output.timeperframe.denominator = denominator;
4590c0d06caSMauro Carvalho Chehab }
4600c0d06caSMauro Carvalho Chehab
4610c0d06caSMauro Carvalho Chehab return 0;
4620c0d06caSMauro Carvalho Chehab }
4630c0d06caSMauro Carvalho Chehab
uvc_v4l2_set_streamparm(struct uvc_streaming * stream,struct v4l2_streamparm * parm)4640c0d06caSMauro Carvalho Chehab static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
4650c0d06caSMauro Carvalho Chehab struct v4l2_streamparm *parm)
4660c0d06caSMauro Carvalho Chehab {
4670c0d06caSMauro Carvalho Chehab struct uvc_streaming_control probe;
4680c0d06caSMauro Carvalho Chehab struct v4l2_fract timeperframe;
469af621ba2SLaurent Pinchart const struct uvc_format *format;
470af621ba2SLaurent Pinchart const struct uvc_frame *frame;
4712c6b222cSLaurent Pinchart u32 interval, maxd;
47207b7d9fcSPhilipp Zabel unsigned int i;
4730c0d06caSMauro Carvalho Chehab int ret;
4740c0d06caSMauro Carvalho Chehab
4750c0d06caSMauro Carvalho Chehab if (parm->type != stream->type)
4760c0d06caSMauro Carvalho Chehab return -EINVAL;
4770c0d06caSMauro Carvalho Chehab
4780c0d06caSMauro Carvalho Chehab if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
4790c0d06caSMauro Carvalho Chehab timeperframe = parm->parm.capture.timeperframe;
4800c0d06caSMauro Carvalho Chehab else
4810c0d06caSMauro Carvalho Chehab timeperframe = parm->parm.output.timeperframe;
4820c0d06caSMauro Carvalho Chehab
4836ba8b8d4SMichael Grzeschik interval = v4l2_fraction_to_interval(timeperframe.numerator,
4840c0d06caSMauro Carvalho Chehab timeperframe.denominator);
4859e56380aSJoe Perches uvc_dbg(stream->dev, FORMAT, "Setting frame interval to %u/%u (%u)\n",
4860c0d06caSMauro Carvalho Chehab timeperframe.numerator, timeperframe.denominator, interval);
4870c0d06caSMauro Carvalho Chehab
4880c0d06caSMauro Carvalho Chehab mutex_lock(&stream->mutex);
4890c0d06caSMauro Carvalho Chehab
4900c0d06caSMauro Carvalho Chehab if (uvc_queue_streaming(&stream->queue)) {
4910c0d06caSMauro Carvalho Chehab mutex_unlock(&stream->mutex);
4920c0d06caSMauro Carvalho Chehab return -EBUSY;
4930c0d06caSMauro Carvalho Chehab }
4940c0d06caSMauro Carvalho Chehab
49507b7d9fcSPhilipp Zabel format = stream->cur_format;
49607b7d9fcSPhilipp Zabel frame = stream->cur_frame;
4978c0d44e2SEzequiel Garcia probe = stream->ctrl;
49807b7d9fcSPhilipp Zabel probe.dwFrameInterval = uvc_try_frame_interval(frame, interval);
4992c6b222cSLaurent Pinchart maxd = abs((s32)probe.dwFrameInterval - interval);
50007b7d9fcSPhilipp Zabel
50107b7d9fcSPhilipp Zabel /* Try frames with matching size to find the best frame interval. */
50207b7d9fcSPhilipp Zabel for (i = 0; i < format->nframes && maxd != 0; i++) {
5032c6b222cSLaurent Pinchart u32 d, ival;
50407b7d9fcSPhilipp Zabel
505aa8db3adSLaurent Pinchart if (&format->frames[i] == stream->cur_frame)
50607b7d9fcSPhilipp Zabel continue;
50707b7d9fcSPhilipp Zabel
508aa8db3adSLaurent Pinchart if (format->frames[i].wWidth != stream->cur_frame->wWidth ||
509aa8db3adSLaurent Pinchart format->frames[i].wHeight != stream->cur_frame->wHeight)
51007b7d9fcSPhilipp Zabel continue;
51107b7d9fcSPhilipp Zabel
512aa8db3adSLaurent Pinchart ival = uvc_try_frame_interval(&format->frames[i], interval);
5132c6b222cSLaurent Pinchart d = abs((s32)ival - interval);
51407b7d9fcSPhilipp Zabel if (d >= maxd)
51507b7d9fcSPhilipp Zabel continue;
51607b7d9fcSPhilipp Zabel
517aa8db3adSLaurent Pinchart frame = &format->frames[i];
51807b7d9fcSPhilipp Zabel probe.bFrameIndex = frame->bFrameIndex;
51907b7d9fcSPhilipp Zabel probe.dwFrameInterval = ival;
52007b7d9fcSPhilipp Zabel maxd = d;
52107b7d9fcSPhilipp Zabel }
5220c0d06caSMauro Carvalho Chehab
5230c0d06caSMauro Carvalho Chehab /* Probe the device with the new settings. */
5240c0d06caSMauro Carvalho Chehab ret = uvc_probe_video(stream, &probe);
5250c0d06caSMauro Carvalho Chehab if (ret < 0) {
5260c0d06caSMauro Carvalho Chehab mutex_unlock(&stream->mutex);
5270c0d06caSMauro Carvalho Chehab return ret;
5280c0d06caSMauro Carvalho Chehab }
5290c0d06caSMauro Carvalho Chehab
5308c0d44e2SEzequiel Garcia stream->ctrl = probe;
53107b7d9fcSPhilipp Zabel stream->cur_frame = frame;
5320c0d06caSMauro Carvalho Chehab mutex_unlock(&stream->mutex);
5330c0d06caSMauro Carvalho Chehab
5340c0d06caSMauro Carvalho Chehab /* Return the actual frame period. */
5350c0d06caSMauro Carvalho Chehab timeperframe.numerator = probe.dwFrameInterval;
5360c0d06caSMauro Carvalho Chehab timeperframe.denominator = 10000000;
5376ba8b8d4SMichael Grzeschik v4l2_simplify_fraction(&timeperframe.numerator,
5380c0d06caSMauro Carvalho Chehab &timeperframe.denominator, 8, 333);
5390c0d06caSMauro Carvalho Chehab
54097a2777aSRicardo Ribalda if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
5410c0d06caSMauro Carvalho Chehab parm->parm.capture.timeperframe = timeperframe;
54297a2777aSRicardo Ribalda parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
54397a2777aSRicardo Ribalda } else {
5440c0d06caSMauro Carvalho Chehab parm->parm.output.timeperframe = timeperframe;
54597a2777aSRicardo Ribalda parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
54697a2777aSRicardo Ribalda }
5470c0d06caSMauro Carvalho Chehab
5480c0d06caSMauro Carvalho Chehab return 0;
5490c0d06caSMauro Carvalho Chehab }
5500c0d06caSMauro Carvalho Chehab
5510c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
5520c0d06caSMauro Carvalho Chehab * Privilege management
5530c0d06caSMauro Carvalho Chehab */
5540c0d06caSMauro Carvalho Chehab
5550c0d06caSMauro Carvalho Chehab /*
5560c0d06caSMauro Carvalho Chehab * Privilege management is the multiple-open implementation basis. The current
5570c0d06caSMauro Carvalho Chehab * implementation is completely transparent for the end-user and doesn't
5580c0d06caSMauro Carvalho Chehab * require explicit use of the VIDIOC_G_PRIORITY and VIDIOC_S_PRIORITY ioctls.
5590c0d06caSMauro Carvalho Chehab * Those ioctls enable finer control on the device (by making possible for a
5600c0d06caSMauro Carvalho Chehab * user to request exclusive access to a device), but are not mature yet.
5610c0d06caSMauro Carvalho Chehab * Switching to the V4L2 priority mechanism might be considered in the future
5620c0d06caSMauro Carvalho Chehab * if this situation changes.
5630c0d06caSMauro Carvalho Chehab *
5640c0d06caSMauro Carvalho Chehab * Each open instance of a UVC device can either be in a privileged or
5650c0d06caSMauro Carvalho Chehab * unprivileged state. Only a single instance can be in a privileged state at
5660c0d06caSMauro Carvalho Chehab * a given time. Trying to perform an operation that requires privileges will
5670c0d06caSMauro Carvalho Chehab * automatically acquire the required privileges if possible, or return -EBUSY
5680c0d06caSMauro Carvalho Chehab * otherwise. Privileges are dismissed when closing the instance or when
5690c0d06caSMauro Carvalho Chehab * freeing the video buffers using VIDIOC_REQBUFS.
5700c0d06caSMauro Carvalho Chehab *
5710c0d06caSMauro Carvalho Chehab * Operations that require privileges are:
5720c0d06caSMauro Carvalho Chehab *
5730c0d06caSMauro Carvalho Chehab * - VIDIOC_S_INPUT
5740c0d06caSMauro Carvalho Chehab * - VIDIOC_S_PARM
5750c0d06caSMauro Carvalho Chehab * - VIDIOC_S_FMT
5760c0d06caSMauro Carvalho Chehab * - VIDIOC_REQBUFS
5770c0d06caSMauro Carvalho Chehab */
uvc_acquire_privileges(struct uvc_fh * handle)5780c0d06caSMauro Carvalho Chehab static int uvc_acquire_privileges(struct uvc_fh *handle)
5790c0d06caSMauro Carvalho Chehab {
5800c0d06caSMauro Carvalho Chehab /* Always succeed if the handle is already privileged. */
5810c0d06caSMauro Carvalho Chehab if (handle->state == UVC_HANDLE_ACTIVE)
5820c0d06caSMauro Carvalho Chehab return 0;
5830c0d06caSMauro Carvalho Chehab
5840c0d06caSMauro Carvalho Chehab /* Check if the device already has a privileged handle. */
5850c0d06caSMauro Carvalho Chehab if (atomic_inc_return(&handle->stream->active) != 1) {
5860c0d06caSMauro Carvalho Chehab atomic_dec(&handle->stream->active);
5870c0d06caSMauro Carvalho Chehab return -EBUSY;
5880c0d06caSMauro Carvalho Chehab }
5890c0d06caSMauro Carvalho Chehab
5900c0d06caSMauro Carvalho Chehab handle->state = UVC_HANDLE_ACTIVE;
5910c0d06caSMauro Carvalho Chehab return 0;
5920c0d06caSMauro Carvalho Chehab }
5930c0d06caSMauro Carvalho Chehab
uvc_dismiss_privileges(struct uvc_fh * handle)5940c0d06caSMauro Carvalho Chehab static void uvc_dismiss_privileges(struct uvc_fh *handle)
5950c0d06caSMauro Carvalho Chehab {
5960c0d06caSMauro Carvalho Chehab if (handle->state == UVC_HANDLE_ACTIVE)
5970c0d06caSMauro Carvalho Chehab atomic_dec(&handle->stream->active);
5980c0d06caSMauro Carvalho Chehab
5990c0d06caSMauro Carvalho Chehab handle->state = UVC_HANDLE_PASSIVE;
6000c0d06caSMauro Carvalho Chehab }
6010c0d06caSMauro Carvalho Chehab
uvc_has_privileges(struct uvc_fh * handle)6020c0d06caSMauro Carvalho Chehab static int uvc_has_privileges(struct uvc_fh *handle)
6030c0d06caSMauro Carvalho Chehab {
6040c0d06caSMauro Carvalho Chehab return handle->state == UVC_HANDLE_ACTIVE;
6050c0d06caSMauro Carvalho Chehab }
6060c0d06caSMauro Carvalho Chehab
6070c0d06caSMauro Carvalho Chehab /* ------------------------------------------------------------------------
6080c0d06caSMauro Carvalho Chehab * V4L2 file operations
6090c0d06caSMauro Carvalho Chehab */
6100c0d06caSMauro Carvalho Chehab
uvc_v4l2_open(struct file * file)6110c0d06caSMauro Carvalho Chehab static int uvc_v4l2_open(struct file *file)
6120c0d06caSMauro Carvalho Chehab {
6130c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream;
6140c0d06caSMauro Carvalho Chehab struct uvc_fh *handle;
6150c0d06caSMauro Carvalho Chehab int ret = 0;
6160c0d06caSMauro Carvalho Chehab
6170c0d06caSMauro Carvalho Chehab stream = video_drvdata(file);
6189e56380aSJoe Perches uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
6190c0d06caSMauro Carvalho Chehab
6200c0d06caSMauro Carvalho Chehab ret = usb_autopm_get_interface(stream->dev->intf);
6210c0d06caSMauro Carvalho Chehab if (ret < 0)
6220c0d06caSMauro Carvalho Chehab return ret;
6230c0d06caSMauro Carvalho Chehab
6240c0d06caSMauro Carvalho Chehab /* Create the device handle. */
625f14d4988SLaurent Pinchart handle = kzalloc(sizeof(*handle), GFP_KERNEL);
6260c0d06caSMauro Carvalho Chehab if (handle == NULL) {
6270c0d06caSMauro Carvalho Chehab usb_autopm_put_interface(stream->dev->intf);
6280c0d06caSMauro Carvalho Chehab return -ENOMEM;
6290c0d06caSMauro Carvalho Chehab }
6300c0d06caSMauro Carvalho Chehab
63117706f56SLaurent Pinchart mutex_lock(&stream->dev->lock);
63217706f56SLaurent Pinchart if (stream->dev->users == 0) {
63317706f56SLaurent Pinchart ret = uvc_status_start(stream->dev, GFP_KERNEL);
6340c0d06caSMauro Carvalho Chehab if (ret < 0) {
63517706f56SLaurent Pinchart mutex_unlock(&stream->dev->lock);
636a82a45f6SOliver Neukum usb_autopm_put_interface(stream->dev->intf);
6370c0d06caSMauro Carvalho Chehab kfree(handle);
6380c0d06caSMauro Carvalho Chehab return ret;
6390c0d06caSMauro Carvalho Chehab }
6400c0d06caSMauro Carvalho Chehab }
6410c0d06caSMauro Carvalho Chehab
64217706f56SLaurent Pinchart stream->dev->users++;
64317706f56SLaurent Pinchart mutex_unlock(&stream->dev->lock);
64417706f56SLaurent Pinchart
645d8da7513SHans Verkuil v4l2_fh_init(&handle->vfh, &stream->vdev);
6460c0d06caSMauro Carvalho Chehab v4l2_fh_add(&handle->vfh);
6470c0d06caSMauro Carvalho Chehab handle->chain = stream->chain;
6480c0d06caSMauro Carvalho Chehab handle->stream = stream;
6490c0d06caSMauro Carvalho Chehab handle->state = UVC_HANDLE_PASSIVE;
6500c0d06caSMauro Carvalho Chehab file->private_data = handle;
6510c0d06caSMauro Carvalho Chehab
6520c0d06caSMauro Carvalho Chehab return 0;
6530c0d06caSMauro Carvalho Chehab }
6540c0d06caSMauro Carvalho Chehab
uvc_v4l2_release(struct file * file)6550c0d06caSMauro Carvalho Chehab static int uvc_v4l2_release(struct file *file)
6560c0d06caSMauro Carvalho Chehab {
6570c0d06caSMauro Carvalho Chehab struct uvc_fh *handle = file->private_data;
6580c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream = handle->stream;
6590c0d06caSMauro Carvalho Chehab
6609e56380aSJoe Perches uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
6610c0d06caSMauro Carvalho Chehab
662*4dbaa738SRicardo Ribalda uvc_ctrl_cleanup_fh(handle);
663*4dbaa738SRicardo Ribalda
6640c0d06caSMauro Carvalho Chehab /* Only free resources if this is a privileged handle. */
6653f02de27SLaurent Pinchart if (uvc_has_privileges(handle))
6663f02de27SLaurent Pinchart uvc_queue_release(&stream->queue);
6670c0d06caSMauro Carvalho Chehab
6680c0d06caSMauro Carvalho Chehab /* Release the file handle. */
6690c0d06caSMauro Carvalho Chehab uvc_dismiss_privileges(handle);
6700c0d06caSMauro Carvalho Chehab v4l2_fh_del(&handle->vfh);
6710c0d06caSMauro Carvalho Chehab v4l2_fh_exit(&handle->vfh);
6720c0d06caSMauro Carvalho Chehab kfree(handle);
6730c0d06caSMauro Carvalho Chehab file->private_data = NULL;
6740c0d06caSMauro Carvalho Chehab
67517706f56SLaurent Pinchart mutex_lock(&stream->dev->lock);
67617706f56SLaurent Pinchart if (--stream->dev->users == 0)
6770c0d06caSMauro Carvalho Chehab uvc_status_stop(stream->dev);
67817706f56SLaurent Pinchart mutex_unlock(&stream->dev->lock);
6790c0d06caSMauro Carvalho Chehab
6800c0d06caSMauro Carvalho Chehab usb_autopm_put_interface(stream->dev->intf);
6810c0d06caSMauro Carvalho Chehab return 0;
6820c0d06caSMauro Carvalho Chehab }
6830c0d06caSMauro Carvalho Chehab
uvc_ioctl_querycap(struct file * file,void * fh,struct v4l2_capability * cap)684d5e90b7aSLaurent Pinchart static int uvc_ioctl_querycap(struct file *file, void *fh,
685d5e90b7aSLaurent Pinchart struct v4l2_capability *cap)
6860c0d06caSMauro Carvalho Chehab {
6870c0d06caSMauro Carvalho Chehab struct uvc_fh *handle = file->private_data;
6880c0d06caSMauro Carvalho Chehab struct uvc_video_chain *chain = handle->chain;
6890c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream = handle->stream;
6900c0d06caSMauro Carvalho Chehab
691c0decac1SMauro Carvalho Chehab strscpy(cap->driver, "uvcvideo", sizeof(cap->driver));
692457e7911SRicardo Ribalda strscpy(cap->card, handle->stream->dev->name, sizeof(cap->card));
693d5e90b7aSLaurent Pinchart usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info));
694f887e99aSLaurent Pinchart cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
695f887e99aSLaurent Pinchart | chain->caps;
696d5e90b7aSLaurent Pinchart
697d5e90b7aSLaurent Pinchart return 0;
6980c0d06caSMauro Carvalho Chehab }
6990c0d06caSMauro Carvalho Chehab
uvc_ioctl_enum_fmt(struct uvc_streaming * stream,struct v4l2_fmtdesc * fmt)700d5e90b7aSLaurent Pinchart static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream,
701d5e90b7aSLaurent Pinchart struct v4l2_fmtdesc *fmt)
7020c0d06caSMauro Carvalho Chehab {
703af621ba2SLaurent Pinchart const struct uvc_format *format;
704d5e90b7aSLaurent Pinchart enum v4l2_buf_type type = fmt->type;
7052c6b222cSLaurent Pinchart u32 index = fmt->index;
7060c0d06caSMauro Carvalho Chehab
707d5e90b7aSLaurent Pinchart if (fmt->type != stream->type || fmt->index >= stream->nformats)
708d5e90b7aSLaurent Pinchart return -EINVAL;
7090c0d06caSMauro Carvalho Chehab
710d5e90b7aSLaurent Pinchart memset(fmt, 0, sizeof(*fmt));
711d5e90b7aSLaurent Pinchart fmt->index = index;
712d5e90b7aSLaurent Pinchart fmt->type = type;
7130c0d06caSMauro Carvalho Chehab
714ccfad4e8SLaurent Pinchart format = &stream->formats[fmt->index];
715d5e90b7aSLaurent Pinchart fmt->flags = 0;
716d5e90b7aSLaurent Pinchart if (format->flags & UVC_FMT_FLAG_COMPRESSED)
717d5e90b7aSLaurent Pinchart fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
718d5e90b7aSLaurent Pinchart fmt->pixelformat = format->fcc;
719d5e90b7aSLaurent Pinchart return 0;
7200c0d06caSMauro Carvalho Chehab }
7210c0d06caSMauro Carvalho Chehab
uvc_ioctl_enum_fmt_vid_cap(struct file * file,void * fh,struct v4l2_fmtdesc * fmt)722d5e90b7aSLaurent Pinchart static int uvc_ioctl_enum_fmt_vid_cap(struct file *file, void *fh,
723d5e90b7aSLaurent Pinchart struct v4l2_fmtdesc *fmt)
7240c0d06caSMauro Carvalho Chehab {
725d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
726d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
7270c0d06caSMauro Carvalho Chehab
728d5e90b7aSLaurent Pinchart return uvc_ioctl_enum_fmt(stream, fmt);
7290c0d06caSMauro Carvalho Chehab }
730d5e90b7aSLaurent Pinchart
uvc_ioctl_enum_fmt_vid_out(struct file * file,void * fh,struct v4l2_fmtdesc * fmt)731d5e90b7aSLaurent Pinchart static int uvc_ioctl_enum_fmt_vid_out(struct file *file, void *fh,
732d5e90b7aSLaurent Pinchart struct v4l2_fmtdesc *fmt)
733d5e90b7aSLaurent Pinchart {
734d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
735d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
736d5e90b7aSLaurent Pinchart
737d5e90b7aSLaurent Pinchart return uvc_ioctl_enum_fmt(stream, fmt);
738d5e90b7aSLaurent Pinchart }
739d5e90b7aSLaurent Pinchart
uvc_ioctl_g_fmt_vid_cap(struct file * file,void * fh,struct v4l2_format * fmt)740d5e90b7aSLaurent Pinchart static int uvc_ioctl_g_fmt_vid_cap(struct file *file, void *fh,
741d5e90b7aSLaurent Pinchart struct v4l2_format *fmt)
742d5e90b7aSLaurent Pinchart {
743d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
744d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
745d5e90b7aSLaurent Pinchart
746d5e90b7aSLaurent Pinchart return uvc_v4l2_get_format(stream, fmt);
747d5e90b7aSLaurent Pinchart }
748d5e90b7aSLaurent Pinchart
uvc_ioctl_g_fmt_vid_out(struct file * file,void * fh,struct v4l2_format * fmt)749d5e90b7aSLaurent Pinchart static int uvc_ioctl_g_fmt_vid_out(struct file *file, void *fh,
750d5e90b7aSLaurent Pinchart struct v4l2_format *fmt)
751d5e90b7aSLaurent Pinchart {
752d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
753d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
754d5e90b7aSLaurent Pinchart
755d5e90b7aSLaurent Pinchart return uvc_v4l2_get_format(stream, fmt);
756d5e90b7aSLaurent Pinchart }
757d5e90b7aSLaurent Pinchart
uvc_ioctl_s_fmt_vid_cap(struct file * file,void * fh,struct v4l2_format * fmt)758d5e90b7aSLaurent Pinchart static int uvc_ioctl_s_fmt_vid_cap(struct file *file, void *fh,
759d5e90b7aSLaurent Pinchart struct v4l2_format *fmt)
760d5e90b7aSLaurent Pinchart {
761d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
762d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
763d5e90b7aSLaurent Pinchart int ret;
764d5e90b7aSLaurent Pinchart
765d5e90b7aSLaurent Pinchart ret = uvc_acquire_privileges(handle);
766d5e90b7aSLaurent Pinchart if (ret < 0)
767d5e90b7aSLaurent Pinchart return ret;
768d5e90b7aSLaurent Pinchart
769d5e90b7aSLaurent Pinchart return uvc_v4l2_set_format(stream, fmt);
770d5e90b7aSLaurent Pinchart }
771d5e90b7aSLaurent Pinchart
uvc_ioctl_s_fmt_vid_out(struct file * file,void * fh,struct v4l2_format * fmt)772d5e90b7aSLaurent Pinchart static int uvc_ioctl_s_fmt_vid_out(struct file *file, void *fh,
773d5e90b7aSLaurent Pinchart struct v4l2_format *fmt)
774d5e90b7aSLaurent Pinchart {
775d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
776d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
777d5e90b7aSLaurent Pinchart int ret;
778d5e90b7aSLaurent Pinchart
779d5e90b7aSLaurent Pinchart ret = uvc_acquire_privileges(handle);
780d5e90b7aSLaurent Pinchart if (ret < 0)
781d5e90b7aSLaurent Pinchart return ret;
782d5e90b7aSLaurent Pinchart
783d5e90b7aSLaurent Pinchart return uvc_v4l2_set_format(stream, fmt);
784d5e90b7aSLaurent Pinchart }
785d5e90b7aSLaurent Pinchart
uvc_ioctl_try_fmt_vid_cap(struct file * file,void * fh,struct v4l2_format * fmt)786d5e90b7aSLaurent Pinchart static int uvc_ioctl_try_fmt_vid_cap(struct file *file, void *fh,
787d5e90b7aSLaurent Pinchart struct v4l2_format *fmt)
788d5e90b7aSLaurent Pinchart {
789d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
790d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
791d5e90b7aSLaurent Pinchart struct uvc_streaming_control probe;
792d5e90b7aSLaurent Pinchart
793d5e90b7aSLaurent Pinchart return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL);
794d5e90b7aSLaurent Pinchart }
795d5e90b7aSLaurent Pinchart
uvc_ioctl_try_fmt_vid_out(struct file * file,void * fh,struct v4l2_format * fmt)796d5e90b7aSLaurent Pinchart static int uvc_ioctl_try_fmt_vid_out(struct file *file, void *fh,
797d5e90b7aSLaurent Pinchart struct v4l2_format *fmt)
798d5e90b7aSLaurent Pinchart {
799d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
800d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
801d5e90b7aSLaurent Pinchart struct uvc_streaming_control probe;
802d5e90b7aSLaurent Pinchart
803d5e90b7aSLaurent Pinchart return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL);
804d5e90b7aSLaurent Pinchart }
805d5e90b7aSLaurent Pinchart
uvc_ioctl_reqbufs(struct file * file,void * fh,struct v4l2_requestbuffers * rb)806d5e90b7aSLaurent Pinchart static int uvc_ioctl_reqbufs(struct file *file, void *fh,
807d5e90b7aSLaurent Pinchart struct v4l2_requestbuffers *rb)
808d5e90b7aSLaurent Pinchart {
809d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
810d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
811d5e90b7aSLaurent Pinchart int ret;
812d5e90b7aSLaurent Pinchart
813d5e90b7aSLaurent Pinchart ret = uvc_acquire_privileges(handle);
814d5e90b7aSLaurent Pinchart if (ret < 0)
815d5e90b7aSLaurent Pinchart return ret;
816d5e90b7aSLaurent Pinchart
817d5e90b7aSLaurent Pinchart mutex_lock(&stream->mutex);
8181b7f9c98SLaurent Pinchart ret = uvc_request_buffers(&stream->queue, rb);
819d5e90b7aSLaurent Pinchart mutex_unlock(&stream->mutex);
820d5e90b7aSLaurent Pinchart if (ret < 0)
821d5e90b7aSLaurent Pinchart return ret;
822d5e90b7aSLaurent Pinchart
8230c0d06caSMauro Carvalho Chehab if (ret == 0)
824d5e90b7aSLaurent Pinchart uvc_dismiss_privileges(handle);
825d5e90b7aSLaurent Pinchart
826d5e90b7aSLaurent Pinchart return 0;
8270c0d06caSMauro Carvalho Chehab }
8280c0d06caSMauro Carvalho Chehab
uvc_ioctl_querybuf(struct file * file,void * fh,struct v4l2_buffer * buf)829d5e90b7aSLaurent Pinchart static int uvc_ioctl_querybuf(struct file *file, void *fh,
830d5e90b7aSLaurent Pinchart struct v4l2_buffer *buf)
8310c0d06caSMauro Carvalho Chehab {
832d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
833d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
8340c0d06caSMauro Carvalho Chehab
835d5e90b7aSLaurent Pinchart if (!uvc_has_privileges(handle))
836d5e90b7aSLaurent Pinchart return -EBUSY;
837d5e90b7aSLaurent Pinchart
838d5e90b7aSLaurent Pinchart return uvc_query_buffer(&stream->queue, buf);
839d5e90b7aSLaurent Pinchart }
840d5e90b7aSLaurent Pinchart
uvc_ioctl_qbuf(struct file * file,void * fh,struct v4l2_buffer * buf)841d5e90b7aSLaurent Pinchart static int uvc_ioctl_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
842d5e90b7aSLaurent Pinchart {
843d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
844d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
845d5e90b7aSLaurent Pinchart
846d5e90b7aSLaurent Pinchart if (!uvc_has_privileges(handle))
847d5e90b7aSLaurent Pinchart return -EBUSY;
848d5e90b7aSLaurent Pinchart
849394dc588SHans Verkuil return uvc_queue_buffer(&stream->queue,
850394dc588SHans Verkuil stream->vdev.v4l2_dev->mdev, buf);
851d5e90b7aSLaurent Pinchart }
852d5e90b7aSLaurent Pinchart
uvc_ioctl_expbuf(struct file * file,void * fh,struct v4l2_exportbuffer * exp)8537195f61bSLaurent Pinchart static int uvc_ioctl_expbuf(struct file *file, void *fh,
8547195f61bSLaurent Pinchart struct v4l2_exportbuffer *exp)
8557195f61bSLaurent Pinchart {
8567195f61bSLaurent Pinchart struct uvc_fh *handle = fh;
8577195f61bSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
8587195f61bSLaurent Pinchart
8597195f61bSLaurent Pinchart if (!uvc_has_privileges(handle))
8607195f61bSLaurent Pinchart return -EBUSY;
8617195f61bSLaurent Pinchart
8627195f61bSLaurent Pinchart return uvc_export_buffer(&stream->queue, exp);
8637195f61bSLaurent Pinchart }
8647195f61bSLaurent Pinchart
uvc_ioctl_dqbuf(struct file * file,void * fh,struct v4l2_buffer * buf)865d5e90b7aSLaurent Pinchart static int uvc_ioctl_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
866d5e90b7aSLaurent Pinchart {
867d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
868d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
869d5e90b7aSLaurent Pinchart
870d5e90b7aSLaurent Pinchart if (!uvc_has_privileges(handle))
871d5e90b7aSLaurent Pinchart return -EBUSY;
872d5e90b7aSLaurent Pinchart
873d5e90b7aSLaurent Pinchart return uvc_dequeue_buffer(&stream->queue, buf,
874d5e90b7aSLaurent Pinchart file->f_flags & O_NONBLOCK);
875d5e90b7aSLaurent Pinchart }
876d5e90b7aSLaurent Pinchart
uvc_ioctl_create_bufs(struct file * file,void * fh,struct v4l2_create_buffers * cb)877d5e90b7aSLaurent Pinchart static int uvc_ioctl_create_bufs(struct file *file, void *fh,
878d5e90b7aSLaurent Pinchart struct v4l2_create_buffers *cb)
879d5e90b7aSLaurent Pinchart {
880d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
881d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
882d5e90b7aSLaurent Pinchart int ret;
883d5e90b7aSLaurent Pinchart
884d5e90b7aSLaurent Pinchart ret = uvc_acquire_privileges(handle);
8850c0d06caSMauro Carvalho Chehab if (ret < 0)
8860c0d06caSMauro Carvalho Chehab return ret;
8870c0d06caSMauro Carvalho Chehab
888d5e90b7aSLaurent Pinchart return uvc_create_buffers(&stream->queue, cb);
8890c0d06caSMauro Carvalho Chehab }
8900c0d06caSMauro Carvalho Chehab
uvc_ioctl_streamon(struct file * file,void * fh,enum v4l2_buf_type type)891d5e90b7aSLaurent Pinchart static int uvc_ioctl_streamon(struct file *file, void *fh,
892d5e90b7aSLaurent Pinchart enum v4l2_buf_type type)
8930c0d06caSMauro Carvalho Chehab {
894d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
895d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
896d5e90b7aSLaurent Pinchart int ret;
8970c0d06caSMauro Carvalho Chehab
898d5e90b7aSLaurent Pinchart if (!uvc_has_privileges(handle))
899d5e90b7aSLaurent Pinchart return -EBUSY;
900d5e90b7aSLaurent Pinchart
901d5e90b7aSLaurent Pinchart mutex_lock(&stream->mutex);
9020da4ab98SLaurent Pinchart ret = uvc_queue_streamon(&stream->queue, type);
903d5e90b7aSLaurent Pinchart mutex_unlock(&stream->mutex);
904d5e90b7aSLaurent Pinchart
9059c016d61SRafael J. Wysocki return ret;
9060c0d06caSMauro Carvalho Chehab }
9070c0d06caSMauro Carvalho Chehab
uvc_ioctl_streamoff(struct file * file,void * fh,enum v4l2_buf_type type)908d5e90b7aSLaurent Pinchart static int uvc_ioctl_streamoff(struct file *file, void *fh,
909d5e90b7aSLaurent Pinchart enum v4l2_buf_type type)
9100c0d06caSMauro Carvalho Chehab {
911d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
912d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
913d5e90b7aSLaurent Pinchart
914d5e90b7aSLaurent Pinchart if (!uvc_has_privileges(handle))
915d5e90b7aSLaurent Pinchart return -EBUSY;
916d5e90b7aSLaurent Pinchart
917d5e90b7aSLaurent Pinchart mutex_lock(&stream->mutex);
9180da4ab98SLaurent Pinchart uvc_queue_streamoff(&stream->queue, type);
919d5e90b7aSLaurent Pinchart mutex_unlock(&stream->mutex);
920d5e90b7aSLaurent Pinchart
921b83bba24SLaurent Pinchart return 0;
922d5e90b7aSLaurent Pinchart }
923d5e90b7aSLaurent Pinchart
uvc_ioctl_enum_input(struct file * file,void * fh,struct v4l2_input * input)924d5e90b7aSLaurent Pinchart static int uvc_ioctl_enum_input(struct file *file, void *fh,
925d5e90b7aSLaurent Pinchart struct v4l2_input *input)
926d5e90b7aSLaurent Pinchart {
927d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
928d5e90b7aSLaurent Pinchart struct uvc_video_chain *chain = handle->chain;
9290c0d06caSMauro Carvalho Chehab const struct uvc_entity *selector = chain->selector;
9300c0d06caSMauro Carvalho Chehab struct uvc_entity *iterm = NULL;
931261f3338SXiaomeng Tong struct uvc_entity *it;
9320c0d06caSMauro Carvalho Chehab u32 index = input->index;
9330c0d06caSMauro Carvalho Chehab
9340c0d06caSMauro Carvalho Chehab if (selector == NULL ||
9350c0d06caSMauro Carvalho Chehab (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
9360c0d06caSMauro Carvalho Chehab if (index != 0)
9370c0d06caSMauro Carvalho Chehab return -EINVAL;
938261f3338SXiaomeng Tong list_for_each_entry(it, &chain->entities, chain) {
939261f3338SXiaomeng Tong if (UVC_ENTITY_IS_ITERM(it)) {
940261f3338SXiaomeng Tong iterm = it;
9410c0d06caSMauro Carvalho Chehab break;
9420c0d06caSMauro Carvalho Chehab }
943261f3338SXiaomeng Tong }
9440c0d06caSMauro Carvalho Chehab } else if (index < selector->bNrInPins) {
945261f3338SXiaomeng Tong list_for_each_entry(it, &chain->entities, chain) {
946261f3338SXiaomeng Tong if (!UVC_ENTITY_IS_ITERM(it))
9470c0d06caSMauro Carvalho Chehab continue;
948261f3338SXiaomeng Tong if (it->id == selector->baSourceID[index]) {
949261f3338SXiaomeng Tong iterm = it;
9500c0d06caSMauro Carvalho Chehab break;
9510c0d06caSMauro Carvalho Chehab }
9520c0d06caSMauro Carvalho Chehab }
953261f3338SXiaomeng Tong }
9540c0d06caSMauro Carvalho Chehab
955261f3338SXiaomeng Tong if (iterm == NULL)
9560c0d06caSMauro Carvalho Chehab return -EINVAL;
9570c0d06caSMauro Carvalho Chehab
958d5e90b7aSLaurent Pinchart memset(input, 0, sizeof(*input));
9590c0d06caSMauro Carvalho Chehab input->index = index;
960c0decac1SMauro Carvalho Chehab strscpy(input->name, iterm->name, sizeof(input->name));
9610c0d06caSMauro Carvalho Chehab if (UVC_ENTITY_TYPE(iterm) == UVC_ITT_CAMERA)
9620c0d06caSMauro Carvalho Chehab input->type = V4L2_INPUT_TYPE_CAMERA;
963d5e90b7aSLaurent Pinchart
964d5e90b7aSLaurent Pinchart return 0;
9650c0d06caSMauro Carvalho Chehab }
9660c0d06caSMauro Carvalho Chehab
uvc_ioctl_g_input(struct file * file,void * fh,unsigned int * input)967d5e90b7aSLaurent Pinchart static int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input)
9680c0d06caSMauro Carvalho Chehab {
969d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
970d5e90b7aSLaurent Pinchart struct uvc_video_chain *chain = handle->chain;
9711a10d7fdSMauro Carvalho Chehab u8 *buf;
972d5e90b7aSLaurent Pinchart int ret;
9730c0d06caSMauro Carvalho Chehab
9740c0d06caSMauro Carvalho Chehab if (chain->selector == NULL ||
9750c0d06caSMauro Carvalho Chehab (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
976d5e90b7aSLaurent Pinchart *input = 0;
977d5e90b7aSLaurent Pinchart return 0;
9780c0d06caSMauro Carvalho Chehab }
9790c0d06caSMauro Carvalho Chehab
9801a10d7fdSMauro Carvalho Chehab buf = kmalloc(1, GFP_KERNEL);
9811a10d7fdSMauro Carvalho Chehab if (!buf)
9821a10d7fdSMauro Carvalho Chehab return -ENOMEM;
9831a10d7fdSMauro Carvalho Chehab
984d5e90b7aSLaurent Pinchart ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, chain->selector->id,
985d5e90b7aSLaurent Pinchart chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL,
9861a10d7fdSMauro Carvalho Chehab buf, 1);
9871a10d7fdSMauro Carvalho Chehab if (!ret)
9881a10d7fdSMauro Carvalho Chehab *input = *buf - 1;
9890c0d06caSMauro Carvalho Chehab
9901a10d7fdSMauro Carvalho Chehab kfree(buf);
9911a10d7fdSMauro Carvalho Chehab
9921a10d7fdSMauro Carvalho Chehab return ret;
9930c0d06caSMauro Carvalho Chehab }
9940c0d06caSMauro Carvalho Chehab
uvc_ioctl_s_input(struct file * file,void * fh,unsigned int input)995d5e90b7aSLaurent Pinchart static int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input)
9960c0d06caSMauro Carvalho Chehab {
997d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
998d5e90b7aSLaurent Pinchart struct uvc_video_chain *chain = handle->chain;
9991a10d7fdSMauro Carvalho Chehab u8 *buf;
1000d5e90b7aSLaurent Pinchart int ret;
10010c0d06caSMauro Carvalho Chehab
1002d5e90b7aSLaurent Pinchart ret = uvc_acquire_privileges(handle);
10030550513cSLaurent Pinchart if (ret < 0)
10040550513cSLaurent Pinchart return ret;
10050550513cSLaurent Pinchart
10060c0d06caSMauro Carvalho Chehab if (chain->selector == NULL ||
10070c0d06caSMauro Carvalho Chehab (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
1008d5e90b7aSLaurent Pinchart if (input)
10090c0d06caSMauro Carvalho Chehab return -EINVAL;
1010d5e90b7aSLaurent Pinchart return 0;
10110c0d06caSMauro Carvalho Chehab }
10120c0d06caSMauro Carvalho Chehab
1013d5e90b7aSLaurent Pinchart if (input >= chain->selector->bNrInPins)
10140c0d06caSMauro Carvalho Chehab return -EINVAL;
10150c0d06caSMauro Carvalho Chehab
10161a10d7fdSMauro Carvalho Chehab buf = kmalloc(1, GFP_KERNEL);
10171a10d7fdSMauro Carvalho Chehab if (!buf)
10181a10d7fdSMauro Carvalho Chehab return -ENOMEM;
10191a10d7fdSMauro Carvalho Chehab
10201a10d7fdSMauro Carvalho Chehab *buf = input + 1;
10211a10d7fdSMauro Carvalho Chehab ret = uvc_query_ctrl(chain->dev, UVC_SET_CUR, chain->selector->id,
1022d5e90b7aSLaurent Pinchart chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL,
10231a10d7fdSMauro Carvalho Chehab buf, 1);
10241a10d7fdSMauro Carvalho Chehab kfree(buf);
10251a10d7fdSMauro Carvalho Chehab
10261a10d7fdSMauro Carvalho Chehab return ret;
10270c0d06caSMauro Carvalho Chehab }
10280c0d06caSMauro Carvalho Chehab
uvc_ioctl_queryctrl(struct file * file,void * fh,struct v4l2_queryctrl * qc)1029d5e90b7aSLaurent Pinchart static int uvc_ioctl_queryctrl(struct file *file, void *fh,
1030d5e90b7aSLaurent Pinchart struct v4l2_queryctrl *qc)
10310c0d06caSMauro Carvalho Chehab {
1032d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
1033d5e90b7aSLaurent Pinchart struct uvc_video_chain *chain = handle->chain;
10340c0d06caSMauro Carvalho Chehab
1035d5e90b7aSLaurent Pinchart return uvc_query_v4l2_ctrl(chain, qc);
10360c0d06caSMauro Carvalho Chehab }
10370c0d06caSMauro Carvalho Chehab
uvc_ioctl_query_ext_ctrl(struct file * file,void * fh,struct v4l2_query_ext_ctrl * qec)1038e183201bSHans Verkuil static int uvc_ioctl_query_ext_ctrl(struct file *file, void *fh,
1039e183201bSHans Verkuil struct v4l2_query_ext_ctrl *qec)
1040e183201bSHans Verkuil {
1041e183201bSHans Verkuil struct uvc_fh *handle = fh;
1042e183201bSHans Verkuil struct uvc_video_chain *chain = handle->chain;
1043e183201bSHans Verkuil struct v4l2_queryctrl qc = { qec->id };
1044e183201bSHans Verkuil int ret;
1045e183201bSHans Verkuil
1046e183201bSHans Verkuil ret = uvc_query_v4l2_ctrl(chain, &qc);
1047e183201bSHans Verkuil if (ret)
1048e183201bSHans Verkuil return ret;
1049e183201bSHans Verkuil
1050e183201bSHans Verkuil qec->id = qc.id;
1051e183201bSHans Verkuil qec->type = qc.type;
1052c0decac1SMauro Carvalho Chehab strscpy(qec->name, qc.name, sizeof(qec->name));
1053e183201bSHans Verkuil qec->minimum = qc.minimum;
1054e183201bSHans Verkuil qec->maximum = qc.maximum;
1055e183201bSHans Verkuil qec->step = qc.step;
1056e183201bSHans Verkuil qec->default_value = qc.default_value;
1057e183201bSHans Verkuil qec->flags = qc.flags;
1058e183201bSHans Verkuil qec->elem_size = 4;
1059e183201bSHans Verkuil qec->elems = 1;
1060e183201bSHans Verkuil qec->nr_of_dims = 0;
1061e183201bSHans Verkuil memset(qec->dims, 0, sizeof(qec->dims));
1062e183201bSHans Verkuil memset(qec->reserved, 0, sizeof(qec->reserved));
1063e183201bSHans Verkuil
1064e183201bSHans Verkuil return 0;
1065e183201bSHans Verkuil }
1066e183201bSHans Verkuil
uvc_ctrl_check_access(struct uvc_video_chain * chain,struct v4l2_ext_controls * ctrls,unsigned long ioctl)1067ee929d5aSRicardo Ribalda static int uvc_ctrl_check_access(struct uvc_video_chain *chain,
1068ee929d5aSRicardo Ribalda struct v4l2_ext_controls *ctrls,
1069ee929d5aSRicardo Ribalda unsigned long ioctl)
1070ee929d5aSRicardo Ribalda {
1071ee929d5aSRicardo Ribalda struct v4l2_ext_control *ctrl = ctrls->controls;
1072ee929d5aSRicardo Ribalda unsigned int i;
1073ee929d5aSRicardo Ribalda int ret = 0;
1074ee929d5aSRicardo Ribalda
1075ee929d5aSRicardo Ribalda for (i = 0; i < ctrls->count; ++ctrl, ++i) {
10769f582f04SHans Verkuil ret = uvc_ctrl_is_accessible(chain, ctrl->id, ctrls, ioctl);
1077ee929d5aSRicardo Ribalda if (ret)
1078ee929d5aSRicardo Ribalda break;
1079ee929d5aSRicardo Ribalda }
1080ee929d5aSRicardo Ribalda
1081ee929d5aSRicardo Ribalda ctrls->error_idx = ioctl == VIDIOC_TRY_EXT_CTRLS ? i : ctrls->count;
1082ee929d5aSRicardo Ribalda
1083ee929d5aSRicardo Ribalda return ret;
1084ee929d5aSRicardo Ribalda }
1085ee929d5aSRicardo Ribalda
uvc_ioctl_g_ext_ctrls(struct file * file,void * fh,struct v4l2_ext_controls * ctrls)1086d5e90b7aSLaurent Pinchart static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
1087d5e90b7aSLaurent Pinchart struct v4l2_ext_controls *ctrls)
1088d5e90b7aSLaurent Pinchart {
1089d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
1090d5e90b7aSLaurent Pinchart struct uvc_video_chain *chain = handle->chain;
1091d5e90b7aSLaurent Pinchart struct v4l2_ext_control *ctrl = ctrls->controls;
1092d5e90b7aSLaurent Pinchart unsigned int i;
1093d5e90b7aSLaurent Pinchart int ret;
1094d5e90b7aSLaurent Pinchart
1095ee929d5aSRicardo Ribalda ret = uvc_ctrl_check_access(chain, ctrls, VIDIOC_G_EXT_CTRLS);
1096ee929d5aSRicardo Ribalda if (ret < 0)
1097ee929d5aSRicardo Ribalda return ret;
1098ee929d5aSRicardo Ribalda
109991739838SRicardo Ribalda if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
110091739838SRicardo Ribalda for (i = 0; i < ctrls->count; ++ctrl, ++i) {
110191739838SRicardo Ribalda struct v4l2_queryctrl qc = { .id = ctrl->id };
110291739838SRicardo Ribalda
110391739838SRicardo Ribalda ret = uvc_query_v4l2_ctrl(chain, &qc);
110491739838SRicardo Ribalda if (ret < 0) {
110591739838SRicardo Ribalda ctrls->error_idx = i;
110691739838SRicardo Ribalda return ret;
110791739838SRicardo Ribalda }
110891739838SRicardo Ribalda
110991739838SRicardo Ribalda ctrl->value = qc.default_value;
111091739838SRicardo Ribalda }
111191739838SRicardo Ribalda
111291739838SRicardo Ribalda return 0;
111391739838SRicardo Ribalda }
111491739838SRicardo Ribalda
1115d5e90b7aSLaurent Pinchart ret = uvc_ctrl_begin(chain);
1116d5e90b7aSLaurent Pinchart if (ret < 0)
1117d5e90b7aSLaurent Pinchart return ret;
1118d5e90b7aSLaurent Pinchart
1119d5e90b7aSLaurent Pinchart for (i = 0; i < ctrls->count; ++ctrl, ++i) {
1120d5e90b7aSLaurent Pinchart ret = uvc_ctrl_get(chain, ctrl);
1121d5e90b7aSLaurent Pinchart if (ret < 0) {
1122d5e90b7aSLaurent Pinchart uvc_ctrl_rollback(handle);
1123d5e90b7aSLaurent Pinchart ctrls->error_idx = i;
1124d5e90b7aSLaurent Pinchart return ret;
1125d5e90b7aSLaurent Pinchart }
1126d5e90b7aSLaurent Pinchart }
1127d5e90b7aSLaurent Pinchart
1128d5e90b7aSLaurent Pinchart ctrls->error_idx = 0;
1129d5e90b7aSLaurent Pinchart
1130d5e90b7aSLaurent Pinchart return uvc_ctrl_rollback(handle);
1131d5e90b7aSLaurent Pinchart }
1132d5e90b7aSLaurent Pinchart
uvc_ioctl_s_try_ext_ctrls(struct uvc_fh * handle,struct v4l2_ext_controls * ctrls,unsigned long ioctl)1133d5e90b7aSLaurent Pinchart static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle,
1134d5e90b7aSLaurent Pinchart struct v4l2_ext_controls *ctrls,
1135ee929d5aSRicardo Ribalda unsigned long ioctl)
1136d5e90b7aSLaurent Pinchart {
1137d5e90b7aSLaurent Pinchart struct v4l2_ext_control *ctrl = ctrls->controls;
1138d5e90b7aSLaurent Pinchart struct uvc_video_chain *chain = handle->chain;
1139d5e90b7aSLaurent Pinchart unsigned int i;
1140d5e90b7aSLaurent Pinchart int ret;
1141d5e90b7aSLaurent Pinchart
1142ee929d5aSRicardo Ribalda ret = uvc_ctrl_check_access(chain, ctrls, ioctl);
1143ee929d5aSRicardo Ribalda if (ret < 0)
1144ee929d5aSRicardo Ribalda return ret;
1145ee929d5aSRicardo Ribalda
1146d5e90b7aSLaurent Pinchart ret = uvc_ctrl_begin(chain);
1147d5e90b7aSLaurent Pinchart if (ret < 0)
1148d5e90b7aSLaurent Pinchart return ret;
1149d5e90b7aSLaurent Pinchart
1150d5e90b7aSLaurent Pinchart for (i = 0; i < ctrls->count; ++ctrl, ++i) {
1151e5225c82SGuennadi Liakhovetski ret = uvc_ctrl_set(handle, ctrl);
1152d5e90b7aSLaurent Pinchart if (ret < 0) {
1153d5e90b7aSLaurent Pinchart uvc_ctrl_rollback(handle);
1154ee929d5aSRicardo Ribalda ctrls->error_idx = ioctl == VIDIOC_S_EXT_CTRLS ?
1155ee929d5aSRicardo Ribalda ctrls->count : i;
1156d5e90b7aSLaurent Pinchart return ret;
1157d5e90b7aSLaurent Pinchart }
1158d5e90b7aSLaurent Pinchart }
1159d5e90b7aSLaurent Pinchart
1160d5e90b7aSLaurent Pinchart ctrls->error_idx = 0;
1161d5e90b7aSLaurent Pinchart
1162ee929d5aSRicardo Ribalda if (ioctl == VIDIOC_S_EXT_CTRLS)
11636350d6a4SRicardo Ribalda return uvc_ctrl_commit(handle, ctrls);
1164d5e90b7aSLaurent Pinchart else
1165d5e90b7aSLaurent Pinchart return uvc_ctrl_rollback(handle);
1166d5e90b7aSLaurent Pinchart }
1167d5e90b7aSLaurent Pinchart
uvc_ioctl_s_ext_ctrls(struct file * file,void * fh,struct v4l2_ext_controls * ctrls)1168d5e90b7aSLaurent Pinchart static int uvc_ioctl_s_ext_ctrls(struct file *file, void *fh,
1169d5e90b7aSLaurent Pinchart struct v4l2_ext_controls *ctrls)
1170d5e90b7aSLaurent Pinchart {
1171d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
1172d5e90b7aSLaurent Pinchart
1173ee929d5aSRicardo Ribalda return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, VIDIOC_S_EXT_CTRLS);
1174d5e90b7aSLaurent Pinchart }
1175d5e90b7aSLaurent Pinchart
uvc_ioctl_try_ext_ctrls(struct file * file,void * fh,struct v4l2_ext_controls * ctrls)1176d5e90b7aSLaurent Pinchart static int uvc_ioctl_try_ext_ctrls(struct file *file, void *fh,
1177d5e90b7aSLaurent Pinchart struct v4l2_ext_controls *ctrls)
1178d5e90b7aSLaurent Pinchart {
1179d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
1180d5e90b7aSLaurent Pinchart
1181ee929d5aSRicardo Ribalda return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, VIDIOC_TRY_EXT_CTRLS);
1182d5e90b7aSLaurent Pinchart }
1183d5e90b7aSLaurent Pinchart
uvc_ioctl_querymenu(struct file * file,void * fh,struct v4l2_querymenu * qm)1184d5e90b7aSLaurent Pinchart static int uvc_ioctl_querymenu(struct file *file, void *fh,
1185d5e90b7aSLaurent Pinchart struct v4l2_querymenu *qm)
1186d5e90b7aSLaurent Pinchart {
1187d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
1188d5e90b7aSLaurent Pinchart struct uvc_video_chain *chain = handle->chain;
1189d5e90b7aSLaurent Pinchart
1190d5e90b7aSLaurent Pinchart return uvc_query_v4l2_menu(chain, qm);
1191d5e90b7aSLaurent Pinchart }
1192d5e90b7aSLaurent Pinchart
uvc_ioctl_g_selection(struct file * file,void * fh,struct v4l2_selection * sel)11931461fe7aSHans Verkuil static int uvc_ioctl_g_selection(struct file *file, void *fh,
11941461fe7aSHans Verkuil struct v4l2_selection *sel)
1195d5e90b7aSLaurent Pinchart {
1196d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
1197d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
1198d5e90b7aSLaurent Pinchart
11991461fe7aSHans Verkuil if (sel->type != stream->type)
1200d5e90b7aSLaurent Pinchart return -EINVAL;
1201d5e90b7aSLaurent Pinchart
12021461fe7aSHans Verkuil switch (sel->target) {
12031461fe7aSHans Verkuil case V4L2_SEL_TGT_CROP_DEFAULT:
12041461fe7aSHans Verkuil case V4L2_SEL_TGT_CROP_BOUNDS:
12051461fe7aSHans Verkuil if (stream->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
12061461fe7aSHans Verkuil return -EINVAL;
12071461fe7aSHans Verkuil break;
12081461fe7aSHans Verkuil case V4L2_SEL_TGT_COMPOSE_DEFAULT:
12091461fe7aSHans Verkuil case V4L2_SEL_TGT_COMPOSE_BOUNDS:
12101461fe7aSHans Verkuil if (stream->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
12111461fe7aSHans Verkuil return -EINVAL;
12121461fe7aSHans Verkuil break;
12131461fe7aSHans Verkuil default:
12141461fe7aSHans Verkuil return -EINVAL;
12151461fe7aSHans Verkuil }
12161461fe7aSHans Verkuil
12171461fe7aSHans Verkuil sel->r.left = 0;
12181461fe7aSHans Verkuil sel->r.top = 0;
1219d5e90b7aSLaurent Pinchart mutex_lock(&stream->mutex);
12201461fe7aSHans Verkuil sel->r.width = stream->cur_frame->wWidth;
12211461fe7aSHans Verkuil sel->r.height = stream->cur_frame->wHeight;
1222d5e90b7aSLaurent Pinchart mutex_unlock(&stream->mutex);
1223d5e90b7aSLaurent Pinchart
1224d5e90b7aSLaurent Pinchart return 0;
1225d5e90b7aSLaurent Pinchart }
1226d5e90b7aSLaurent Pinchart
uvc_ioctl_g_parm(struct file * file,void * fh,struct v4l2_streamparm * parm)1227d5e90b7aSLaurent Pinchart static int uvc_ioctl_g_parm(struct file *file, void *fh,
1228d5e90b7aSLaurent Pinchart struct v4l2_streamparm *parm)
1229d5e90b7aSLaurent Pinchart {
1230d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
1231d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
1232d5e90b7aSLaurent Pinchart
1233d5e90b7aSLaurent Pinchart return uvc_v4l2_get_streamparm(stream, parm);
1234d5e90b7aSLaurent Pinchart }
1235d5e90b7aSLaurent Pinchart
uvc_ioctl_s_parm(struct file * file,void * fh,struct v4l2_streamparm * parm)1236d5e90b7aSLaurent Pinchart static int uvc_ioctl_s_parm(struct file *file, void *fh,
1237d5e90b7aSLaurent Pinchart struct v4l2_streamparm *parm)
1238d5e90b7aSLaurent Pinchart {
1239d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
1240d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
1241d5e90b7aSLaurent Pinchart int ret;
1242d5e90b7aSLaurent Pinchart
1243d5e90b7aSLaurent Pinchart ret = uvc_acquire_privileges(handle);
1244d5e90b7aSLaurent Pinchart if (ret < 0)
1245d5e90b7aSLaurent Pinchart return ret;
1246d5e90b7aSLaurent Pinchart
1247d5e90b7aSLaurent Pinchart return uvc_v4l2_set_streamparm(stream, parm);
1248d5e90b7aSLaurent Pinchart }
1249d5e90b7aSLaurent Pinchart
uvc_ioctl_enum_framesizes(struct file * file,void * fh,struct v4l2_frmsizeenum * fsize)1250d5e90b7aSLaurent Pinchart static int uvc_ioctl_enum_framesizes(struct file *file, void *fh,
1251d5e90b7aSLaurent Pinchart struct v4l2_frmsizeenum *fsize)
1252d5e90b7aSLaurent Pinchart {
1253d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
1254d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
1255af621ba2SLaurent Pinchart const struct uvc_format *format = NULL;
1256af621ba2SLaurent Pinchart const struct uvc_frame *frame = NULL;
125707b7d9fcSPhilipp Zabel unsigned int index;
125807b7d9fcSPhilipp Zabel unsigned int i;
12590c0d06caSMauro Carvalho Chehab
12600c0d06caSMauro Carvalho Chehab /* Look for the given pixel format */
12610c0d06caSMauro Carvalho Chehab for (i = 0; i < stream->nformats; i++) {
1262ccfad4e8SLaurent Pinchart if (stream->formats[i].fcc == fsize->pixel_format) {
1263ccfad4e8SLaurent Pinchart format = &stream->formats[i];
12640c0d06caSMauro Carvalho Chehab break;
12650c0d06caSMauro Carvalho Chehab }
12660c0d06caSMauro Carvalho Chehab }
12670c0d06caSMauro Carvalho Chehab if (format == NULL)
12680c0d06caSMauro Carvalho Chehab return -EINVAL;
12690c0d06caSMauro Carvalho Chehab
127007b7d9fcSPhilipp Zabel /* Skip duplicate frame sizes */
127107b7d9fcSPhilipp Zabel for (i = 0, index = 0; i < format->nframes; i++) {
1272aa8db3adSLaurent Pinchart if (frame && frame->wWidth == format->frames[i].wWidth &&
1273aa8db3adSLaurent Pinchart frame->wHeight == format->frames[i].wHeight)
127407b7d9fcSPhilipp Zabel continue;
1275aa8db3adSLaurent Pinchart frame = &format->frames[i];
127607b7d9fcSPhilipp Zabel if (index == fsize->index)
127707b7d9fcSPhilipp Zabel break;
127807b7d9fcSPhilipp Zabel index++;
127907b7d9fcSPhilipp Zabel }
128007b7d9fcSPhilipp Zabel
128107b7d9fcSPhilipp Zabel if (i == format->nframes)
12820c0d06caSMauro Carvalho Chehab return -EINVAL;
12830c0d06caSMauro Carvalho Chehab
12840c0d06caSMauro Carvalho Chehab fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
12850c0d06caSMauro Carvalho Chehab fsize->discrete.width = frame->wWidth;
12860c0d06caSMauro Carvalho Chehab fsize->discrete.height = frame->wHeight;
1287d5e90b7aSLaurent Pinchart return 0;
12880c0d06caSMauro Carvalho Chehab }
12890c0d06caSMauro Carvalho Chehab
uvc_ioctl_enum_frameintervals(struct file * file,void * fh,struct v4l2_frmivalenum * fival)1290d5e90b7aSLaurent Pinchart static int uvc_ioctl_enum_frameintervals(struct file *file, void *fh,
1291d5e90b7aSLaurent Pinchart struct v4l2_frmivalenum *fival)
12920c0d06caSMauro Carvalho Chehab {
1293d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
1294d5e90b7aSLaurent Pinchart struct uvc_streaming *stream = handle->stream;
1295af621ba2SLaurent Pinchart const struct uvc_format *format = NULL;
1296af621ba2SLaurent Pinchart const struct uvc_frame *frame = NULL;
129707b7d9fcSPhilipp Zabel unsigned int nintervals;
129807b7d9fcSPhilipp Zabel unsigned int index;
129907b7d9fcSPhilipp Zabel unsigned int i;
13000c0d06caSMauro Carvalho Chehab
13010c0d06caSMauro Carvalho Chehab /* Look for the given pixel format and frame size */
13020c0d06caSMauro Carvalho Chehab for (i = 0; i < stream->nformats; i++) {
1303ccfad4e8SLaurent Pinchart if (stream->formats[i].fcc == fival->pixel_format) {
1304ccfad4e8SLaurent Pinchart format = &stream->formats[i];
13050c0d06caSMauro Carvalho Chehab break;
13060c0d06caSMauro Carvalho Chehab }
13070c0d06caSMauro Carvalho Chehab }
13080c0d06caSMauro Carvalho Chehab if (format == NULL)
13090c0d06caSMauro Carvalho Chehab return -EINVAL;
13100c0d06caSMauro Carvalho Chehab
131107b7d9fcSPhilipp Zabel index = fival->index;
13120c0d06caSMauro Carvalho Chehab for (i = 0; i < format->nframes; i++) {
1313aa8db3adSLaurent Pinchart if (format->frames[i].wWidth == fival->width &&
1314aa8db3adSLaurent Pinchart format->frames[i].wHeight == fival->height) {
1315aa8db3adSLaurent Pinchart frame = &format->frames[i];
131607b7d9fcSPhilipp Zabel nintervals = frame->bFrameIntervalType ?: 1;
131707b7d9fcSPhilipp Zabel if (index < nintervals)
13180c0d06caSMauro Carvalho Chehab break;
131907b7d9fcSPhilipp Zabel index -= nintervals;
13200c0d06caSMauro Carvalho Chehab }
13210c0d06caSMauro Carvalho Chehab }
132207b7d9fcSPhilipp Zabel if (i == format->nframes)
13230c0d06caSMauro Carvalho Chehab return -EINVAL;
13240c0d06caSMauro Carvalho Chehab
13250c0d06caSMauro Carvalho Chehab if (frame->bFrameIntervalType) {
13260c0d06caSMauro Carvalho Chehab fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
13270c0d06caSMauro Carvalho Chehab fival->discrete.numerator =
132807b7d9fcSPhilipp Zabel frame->dwFrameInterval[index];
13290c0d06caSMauro Carvalho Chehab fival->discrete.denominator = 10000000;
13306ba8b8d4SMichael Grzeschik v4l2_simplify_fraction(&fival->discrete.numerator,
13310c0d06caSMauro Carvalho Chehab &fival->discrete.denominator, 8, 333);
13320c0d06caSMauro Carvalho Chehab } else {
13330c0d06caSMauro Carvalho Chehab fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
1334d5e90b7aSLaurent Pinchart fival->stepwise.min.numerator = frame->dwFrameInterval[0];
13350c0d06caSMauro Carvalho Chehab fival->stepwise.min.denominator = 10000000;
1336d5e90b7aSLaurent Pinchart fival->stepwise.max.numerator = frame->dwFrameInterval[1];
13370c0d06caSMauro Carvalho Chehab fival->stepwise.max.denominator = 10000000;
1338d5e90b7aSLaurent Pinchart fival->stepwise.step.numerator = frame->dwFrameInterval[2];
13390c0d06caSMauro Carvalho Chehab fival->stepwise.step.denominator = 10000000;
13406ba8b8d4SMichael Grzeschik v4l2_simplify_fraction(&fival->stepwise.min.numerator,
13410c0d06caSMauro Carvalho Chehab &fival->stepwise.min.denominator, 8, 333);
13426ba8b8d4SMichael Grzeschik v4l2_simplify_fraction(&fival->stepwise.max.numerator,
13430c0d06caSMauro Carvalho Chehab &fival->stepwise.max.denominator, 8, 333);
13446ba8b8d4SMichael Grzeschik v4l2_simplify_fraction(&fival->stepwise.step.numerator,
13450c0d06caSMauro Carvalho Chehab &fival->stepwise.step.denominator, 8, 333);
13460c0d06caSMauro Carvalho Chehab }
1347d5e90b7aSLaurent Pinchart
1348d5e90b7aSLaurent Pinchart return 0;
13490c0d06caSMauro Carvalho Chehab }
13500c0d06caSMauro Carvalho Chehab
uvc_ioctl_subscribe_event(struct v4l2_fh * fh,const struct v4l2_event_subscription * sub)1351d5e90b7aSLaurent Pinchart static int uvc_ioctl_subscribe_event(struct v4l2_fh *fh,
1352d5e90b7aSLaurent Pinchart const struct v4l2_event_subscription *sub)
13530c0d06caSMauro Carvalho Chehab {
13540c0d06caSMauro Carvalho Chehab switch (sub->type) {
13550c0d06caSMauro Carvalho Chehab case V4L2_EVENT_CTRL:
1356d5e90b7aSLaurent Pinchart return v4l2_event_subscribe(fh, sub, 0, &uvc_ctrl_sub_ev_ops);
13570c0d06caSMauro Carvalho Chehab default:
13580c0d06caSMauro Carvalho Chehab return -EINVAL;
13590c0d06caSMauro Carvalho Chehab }
13600c0d06caSMauro Carvalho Chehab }
13610c0d06caSMauro Carvalho Chehab
uvc_ioctl_default(struct file * file,void * fh,bool valid_prio,unsigned int cmd,void * arg)1362d5e90b7aSLaurent Pinchart static long uvc_ioctl_default(struct file *file, void *fh, bool valid_prio,
1363d5e90b7aSLaurent Pinchart unsigned int cmd, void *arg)
1364d5e90b7aSLaurent Pinchart {
1365d5e90b7aSLaurent Pinchart struct uvc_fh *handle = fh;
1366d5e90b7aSLaurent Pinchart struct uvc_video_chain *chain = handle->chain;
13670c0d06caSMauro Carvalho Chehab
1368d5e90b7aSLaurent Pinchart switch (cmd) {
1369d5e90b7aSLaurent Pinchart /* Dynamic controls. */
13700c0d06caSMauro Carvalho Chehab case UVCIOC_CTRL_MAP:
1371716c3304SRicardo Ribalda return uvc_ioctl_xu_ctrl_map(chain, arg);
13720c0d06caSMauro Carvalho Chehab
13730c0d06caSMauro Carvalho Chehab case UVCIOC_CTRL_QUERY:
13740c0d06caSMauro Carvalho Chehab return uvc_xu_ctrl_query(chain, arg);
13750c0d06caSMauro Carvalho Chehab
13760c0d06caSMauro Carvalho Chehab default:
13770c0d06caSMauro Carvalho Chehab return -ENOTTY;
13780c0d06caSMauro Carvalho Chehab }
13790c0d06caSMauro Carvalho Chehab }
13800c0d06caSMauro Carvalho Chehab
13810c0d06caSMauro Carvalho Chehab #ifdef CONFIG_COMPAT
13820c0d06caSMauro Carvalho Chehab struct uvc_xu_control_mapping32 {
13832c6b222cSLaurent Pinchart u32 id;
13842c6b222cSLaurent Pinchart u8 name[32];
13852c6b222cSLaurent Pinchart u8 entity[16];
13862c6b222cSLaurent Pinchart u8 selector;
13870c0d06caSMauro Carvalho Chehab
13882c6b222cSLaurent Pinchart u8 size;
13892c6b222cSLaurent Pinchart u8 offset;
13902c6b222cSLaurent Pinchart u32 v4l2_type;
13912c6b222cSLaurent Pinchart u32 data_type;
13920c0d06caSMauro Carvalho Chehab
13930c0d06caSMauro Carvalho Chehab compat_caddr_t menu_info;
13942c6b222cSLaurent Pinchart u32 menu_count;
13950c0d06caSMauro Carvalho Chehab
13962c6b222cSLaurent Pinchart u32 reserved[4];
13970c0d06caSMauro Carvalho Chehab };
13980c0d06caSMauro Carvalho Chehab
uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping * kp,const struct uvc_xu_control_mapping32 __user * up)13990c0d06caSMauro Carvalho Chehab static int uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping *kp,
14000c0d06caSMauro Carvalho Chehab const struct uvc_xu_control_mapping32 __user *up)
14010c0d06caSMauro Carvalho Chehab {
140218e2ea5cSAl Viro struct uvc_xu_control_mapping32 *p = (void *)kp;
140318e2ea5cSAl Viro compat_caddr_t info;
140418e2ea5cSAl Viro u32 count;
14050c0d06caSMauro Carvalho Chehab
140618e2ea5cSAl Viro if (copy_from_user(p, up, sizeof(*p)))
14070c0d06caSMauro Carvalho Chehab return -EFAULT;
14080c0d06caSMauro Carvalho Chehab
140918e2ea5cSAl Viro count = p->menu_count;
141018e2ea5cSAl Viro info = p->menu_info;
141118e2ea5cSAl Viro
14120c0d06caSMauro Carvalho Chehab memset(kp->reserved, 0, sizeof(kp->reserved));
141318e2ea5cSAl Viro kp->menu_info = count ? compat_ptr(info) : NULL;
141418e2ea5cSAl Viro kp->menu_count = count;
14150c0d06caSMauro Carvalho Chehab return 0;
14160c0d06caSMauro Carvalho Chehab }
14170c0d06caSMauro Carvalho Chehab
uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping * kp,struct uvc_xu_control_mapping32 __user * up)14180c0d06caSMauro Carvalho Chehab static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp,
14190c0d06caSMauro Carvalho Chehab struct uvc_xu_control_mapping32 __user *up)
14200c0d06caSMauro Carvalho Chehab {
142118e2ea5cSAl Viro if (copy_to_user(up, kp, offsetof(typeof(*up), menu_info)) ||
142218e2ea5cSAl Viro put_user(kp->menu_count, &up->menu_count))
14230c0d06caSMauro Carvalho Chehab return -EFAULT;
14240c0d06caSMauro Carvalho Chehab
142518e2ea5cSAl Viro if (clear_user(up->reserved, sizeof(up->reserved)))
14260c0d06caSMauro Carvalho Chehab return -EFAULT;
14270c0d06caSMauro Carvalho Chehab
14280c0d06caSMauro Carvalho Chehab return 0;
14290c0d06caSMauro Carvalho Chehab }
14300c0d06caSMauro Carvalho Chehab
14310c0d06caSMauro Carvalho Chehab struct uvc_xu_control_query32 {
14322c6b222cSLaurent Pinchart u8 unit;
14332c6b222cSLaurent Pinchart u8 selector;
14342c6b222cSLaurent Pinchart u8 query;
14352c6b222cSLaurent Pinchart u16 size;
14360c0d06caSMauro Carvalho Chehab compat_caddr_t data;
14370c0d06caSMauro Carvalho Chehab };
14380c0d06caSMauro Carvalho Chehab
uvc_v4l2_get_xu_query(struct uvc_xu_control_query * kp,const struct uvc_xu_control_query32 __user * up)14390c0d06caSMauro Carvalho Chehab static int uvc_v4l2_get_xu_query(struct uvc_xu_control_query *kp,
14400c0d06caSMauro Carvalho Chehab const struct uvc_xu_control_query32 __user *up)
14410c0d06caSMauro Carvalho Chehab {
144218e2ea5cSAl Viro struct uvc_xu_control_query32 v;
14430c0d06caSMauro Carvalho Chehab
144418e2ea5cSAl Viro if (copy_from_user(&v, up, sizeof(v)))
14450c0d06caSMauro Carvalho Chehab return -EFAULT;
14460c0d06caSMauro Carvalho Chehab
144718e2ea5cSAl Viro *kp = (struct uvc_xu_control_query){
144818e2ea5cSAl Viro .unit = v.unit,
144918e2ea5cSAl Viro .selector = v.selector,
145018e2ea5cSAl Viro .query = v.query,
145118e2ea5cSAl Viro .size = v.size,
145218e2ea5cSAl Viro .data = v.size ? compat_ptr(v.data) : NULL
145318e2ea5cSAl Viro };
14540c0d06caSMauro Carvalho Chehab return 0;
14550c0d06caSMauro Carvalho Chehab }
14560c0d06caSMauro Carvalho Chehab
uvc_v4l2_put_xu_query(const struct uvc_xu_control_query * kp,struct uvc_xu_control_query32 __user * up)14570c0d06caSMauro Carvalho Chehab static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp,
14580c0d06caSMauro Carvalho Chehab struct uvc_xu_control_query32 __user *up)
14590c0d06caSMauro Carvalho Chehab {
146018e2ea5cSAl Viro if (copy_to_user(up, kp, offsetof(typeof(*up), data)))
14610c0d06caSMauro Carvalho Chehab return -EFAULT;
14620c0d06caSMauro Carvalho Chehab return 0;
14630c0d06caSMauro Carvalho Chehab }
14640c0d06caSMauro Carvalho Chehab
14650c0d06caSMauro Carvalho Chehab #define UVCIOC_CTRL_MAP32 _IOWR('u', 0x20, struct uvc_xu_control_mapping32)
14660c0d06caSMauro Carvalho Chehab #define UVCIOC_CTRL_QUERY32 _IOWR('u', 0x21, struct uvc_xu_control_query32)
14670c0d06caSMauro Carvalho Chehab
uvc_v4l2_compat_ioctl32(struct file * file,unsigned int cmd,unsigned long arg)14680c0d06caSMauro Carvalho Chehab static long uvc_v4l2_compat_ioctl32(struct file *file,
14690c0d06caSMauro Carvalho Chehab unsigned int cmd, unsigned long arg)
14700c0d06caSMauro Carvalho Chehab {
1471a44323e2SAndy Lutomirski struct uvc_fh *handle = file->private_data;
14720c0d06caSMauro Carvalho Chehab union {
14730c0d06caSMauro Carvalho Chehab struct uvc_xu_control_mapping xmap;
14740c0d06caSMauro Carvalho Chehab struct uvc_xu_control_query xqry;
14750c0d06caSMauro Carvalho Chehab } karg;
14760c0d06caSMauro Carvalho Chehab void __user *up = compat_ptr(arg);
14770c0d06caSMauro Carvalho Chehab long ret;
14780c0d06caSMauro Carvalho Chehab
14790c0d06caSMauro Carvalho Chehab switch (cmd) {
14800c0d06caSMauro Carvalho Chehab case UVCIOC_CTRL_MAP32:
14810c0d06caSMauro Carvalho Chehab ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
1482a44323e2SAndy Lutomirski if (ret)
1483a44323e2SAndy Lutomirski return ret;
1484716c3304SRicardo Ribalda ret = uvc_ioctl_xu_ctrl_map(handle->chain, &karg.xmap);
1485a44323e2SAndy Lutomirski if (ret)
1486a44323e2SAndy Lutomirski return ret;
1487a44323e2SAndy Lutomirski ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up);
1488a44323e2SAndy Lutomirski if (ret)
1489a44323e2SAndy Lutomirski return ret;
1490a44323e2SAndy Lutomirski
14910c0d06caSMauro Carvalho Chehab break;
14920c0d06caSMauro Carvalho Chehab
14930c0d06caSMauro Carvalho Chehab case UVCIOC_CTRL_QUERY32:
14940c0d06caSMauro Carvalho Chehab ret = uvc_v4l2_get_xu_query(&karg.xqry, up);
1495a44323e2SAndy Lutomirski if (ret)
1496a44323e2SAndy Lutomirski return ret;
1497a44323e2SAndy Lutomirski ret = uvc_xu_ctrl_query(handle->chain, &karg.xqry);
1498a44323e2SAndy Lutomirski if (ret)
1499a44323e2SAndy Lutomirski return ret;
1500a44323e2SAndy Lutomirski ret = uvc_v4l2_put_xu_query(&karg.xqry, up);
1501a44323e2SAndy Lutomirski if (ret)
1502a44323e2SAndy Lutomirski return ret;
15030c0d06caSMauro Carvalho Chehab break;
15040c0d06caSMauro Carvalho Chehab
15050c0d06caSMauro Carvalho Chehab default:
15060c0d06caSMauro Carvalho Chehab return -ENOIOCTLCMD;
15070c0d06caSMauro Carvalho Chehab }
15080c0d06caSMauro Carvalho Chehab
15090c0d06caSMauro Carvalho Chehab return ret;
15100c0d06caSMauro Carvalho Chehab }
15110c0d06caSMauro Carvalho Chehab #endif
15120c0d06caSMauro Carvalho Chehab
uvc_v4l2_read(struct file * file,char __user * data,size_t count,loff_t * ppos)15130c0d06caSMauro Carvalho Chehab static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
15140c0d06caSMauro Carvalho Chehab size_t count, loff_t *ppos)
15150c0d06caSMauro Carvalho Chehab {
1516ed4c5fa4SRicardo Ribalda struct uvc_fh *handle = file->private_data;
1517ed4c5fa4SRicardo Ribalda struct uvc_streaming *stream = handle->stream;
1518ed4c5fa4SRicardo Ribalda
15199e56380aSJoe Perches uvc_dbg(stream->dev, CALLS, "%s: not implemented\n", __func__);
15200c0d06caSMauro Carvalho Chehab return -EINVAL;
15210c0d06caSMauro Carvalho Chehab }
15220c0d06caSMauro Carvalho Chehab
uvc_v4l2_mmap(struct file * file,struct vm_area_struct * vma)15230c0d06caSMauro Carvalho Chehab static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
15240c0d06caSMauro Carvalho Chehab {
15250c0d06caSMauro Carvalho Chehab struct uvc_fh *handle = file->private_data;
15260c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream = handle->stream;
15270c0d06caSMauro Carvalho Chehab
15289e56380aSJoe Perches uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
15290c0d06caSMauro Carvalho Chehab
15300c0d06caSMauro Carvalho Chehab return uvc_queue_mmap(&stream->queue, vma);
15310c0d06caSMauro Carvalho Chehab }
15320c0d06caSMauro Carvalho Chehab
uvc_v4l2_poll(struct file * file,poll_table * wait)1533c23e0cb8SAl Viro static __poll_t uvc_v4l2_poll(struct file *file, poll_table *wait)
15340c0d06caSMauro Carvalho Chehab {
15350c0d06caSMauro Carvalho Chehab struct uvc_fh *handle = file->private_data;
15360c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream = handle->stream;
15370c0d06caSMauro Carvalho Chehab
15389e56380aSJoe Perches uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
15390c0d06caSMauro Carvalho Chehab
15400c0d06caSMauro Carvalho Chehab return uvc_queue_poll(&stream->queue, file, wait);
15410c0d06caSMauro Carvalho Chehab }
15420c0d06caSMauro Carvalho Chehab
15430c0d06caSMauro Carvalho Chehab #ifndef CONFIG_MMU
uvc_v4l2_get_unmapped_area(struct file * file,unsigned long addr,unsigned long len,unsigned long pgoff,unsigned long flags)15440c0d06caSMauro Carvalho Chehab static unsigned long uvc_v4l2_get_unmapped_area(struct file *file,
15450c0d06caSMauro Carvalho Chehab unsigned long addr, unsigned long len, unsigned long pgoff,
15460c0d06caSMauro Carvalho Chehab unsigned long flags)
15470c0d06caSMauro Carvalho Chehab {
15480c0d06caSMauro Carvalho Chehab struct uvc_fh *handle = file->private_data;
15490c0d06caSMauro Carvalho Chehab struct uvc_streaming *stream = handle->stream;
15500c0d06caSMauro Carvalho Chehab
15519e56380aSJoe Perches uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
15520c0d06caSMauro Carvalho Chehab
15530c0d06caSMauro Carvalho Chehab return uvc_queue_get_unmapped_area(&stream->queue, pgoff);
15540c0d06caSMauro Carvalho Chehab }
15550c0d06caSMauro Carvalho Chehab #endif
15560c0d06caSMauro Carvalho Chehab
1557d5e90b7aSLaurent Pinchart const struct v4l2_ioctl_ops uvc_ioctl_ops = {
1558d5e90b7aSLaurent Pinchart .vidioc_querycap = uvc_ioctl_querycap,
1559d5e90b7aSLaurent Pinchart .vidioc_enum_fmt_vid_cap = uvc_ioctl_enum_fmt_vid_cap,
1560d5e90b7aSLaurent Pinchart .vidioc_enum_fmt_vid_out = uvc_ioctl_enum_fmt_vid_out,
1561d5e90b7aSLaurent Pinchart .vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt_vid_cap,
1562d5e90b7aSLaurent Pinchart .vidioc_g_fmt_vid_out = uvc_ioctl_g_fmt_vid_out,
1563d5e90b7aSLaurent Pinchart .vidioc_s_fmt_vid_cap = uvc_ioctl_s_fmt_vid_cap,
1564d5e90b7aSLaurent Pinchart .vidioc_s_fmt_vid_out = uvc_ioctl_s_fmt_vid_out,
1565d5e90b7aSLaurent Pinchart .vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt_vid_cap,
1566d5e90b7aSLaurent Pinchart .vidioc_try_fmt_vid_out = uvc_ioctl_try_fmt_vid_out,
1567d5e90b7aSLaurent Pinchart .vidioc_reqbufs = uvc_ioctl_reqbufs,
1568d5e90b7aSLaurent Pinchart .vidioc_querybuf = uvc_ioctl_querybuf,
1569d5e90b7aSLaurent Pinchart .vidioc_qbuf = uvc_ioctl_qbuf,
15707195f61bSLaurent Pinchart .vidioc_expbuf = uvc_ioctl_expbuf,
1571d5e90b7aSLaurent Pinchart .vidioc_dqbuf = uvc_ioctl_dqbuf,
1572d5e90b7aSLaurent Pinchart .vidioc_create_bufs = uvc_ioctl_create_bufs,
1573d5e90b7aSLaurent Pinchart .vidioc_streamon = uvc_ioctl_streamon,
1574d5e90b7aSLaurent Pinchart .vidioc_streamoff = uvc_ioctl_streamoff,
1575d5e90b7aSLaurent Pinchart .vidioc_enum_input = uvc_ioctl_enum_input,
1576d5e90b7aSLaurent Pinchart .vidioc_g_input = uvc_ioctl_g_input,
1577d5e90b7aSLaurent Pinchart .vidioc_s_input = uvc_ioctl_s_input,
1578d5e90b7aSLaurent Pinchart .vidioc_queryctrl = uvc_ioctl_queryctrl,
1579e183201bSHans Verkuil .vidioc_query_ext_ctrl = uvc_ioctl_query_ext_ctrl,
1580d5e90b7aSLaurent Pinchart .vidioc_g_ext_ctrls = uvc_ioctl_g_ext_ctrls,
1581d5e90b7aSLaurent Pinchart .vidioc_s_ext_ctrls = uvc_ioctl_s_ext_ctrls,
1582d5e90b7aSLaurent Pinchart .vidioc_try_ext_ctrls = uvc_ioctl_try_ext_ctrls,
1583d5e90b7aSLaurent Pinchart .vidioc_querymenu = uvc_ioctl_querymenu,
15841461fe7aSHans Verkuil .vidioc_g_selection = uvc_ioctl_g_selection,
1585d5e90b7aSLaurent Pinchart .vidioc_g_parm = uvc_ioctl_g_parm,
1586d5e90b7aSLaurent Pinchart .vidioc_s_parm = uvc_ioctl_s_parm,
1587d5e90b7aSLaurent Pinchart .vidioc_enum_framesizes = uvc_ioctl_enum_framesizes,
1588d5e90b7aSLaurent Pinchart .vidioc_enum_frameintervals = uvc_ioctl_enum_frameintervals,
1589d5e90b7aSLaurent Pinchart .vidioc_subscribe_event = uvc_ioctl_subscribe_event,
1590d5e90b7aSLaurent Pinchart .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
1591d5e90b7aSLaurent Pinchart .vidioc_default = uvc_ioctl_default,
1592d5e90b7aSLaurent Pinchart };
1593d5e90b7aSLaurent Pinchart
15940c0d06caSMauro Carvalho Chehab const struct v4l2_file_operations uvc_fops = {
15950c0d06caSMauro Carvalho Chehab .owner = THIS_MODULE,
15960c0d06caSMauro Carvalho Chehab .open = uvc_v4l2_open,
15970c0d06caSMauro Carvalho Chehab .release = uvc_v4l2_release,
1598d5e90b7aSLaurent Pinchart .unlocked_ioctl = video_ioctl2,
15990c0d06caSMauro Carvalho Chehab #ifdef CONFIG_COMPAT
16000c0d06caSMauro Carvalho Chehab .compat_ioctl32 = uvc_v4l2_compat_ioctl32,
16010c0d06caSMauro Carvalho Chehab #endif
16020c0d06caSMauro Carvalho Chehab .read = uvc_v4l2_read,
16030c0d06caSMauro Carvalho Chehab .mmap = uvc_v4l2_mmap,
16040c0d06caSMauro Carvalho Chehab .poll = uvc_v4l2_poll,
16050c0d06caSMauro Carvalho Chehab #ifndef CONFIG_MMU
16060c0d06caSMauro Carvalho Chehab .get_unmapped_area = uvc_v4l2_get_unmapped_area,
16070c0d06caSMauro Carvalho Chehab #endif
16080c0d06caSMauro Carvalho Chehab };
16090c0d06caSMauro Carvalho Chehab
1610