xref: /openbmc/linux/drivers/media/usb/uvc/uvc_v4l2.c (revision d37cf9b63113f13d742713881ce691fc615d8b3b)
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