18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *      uvc_v4l2.c  --  USB Video Class driver - V4L2 API
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *      Copyright (C) 2005-2010
68c2ecf20Sopenharmony_ci *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/compat.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/list.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/usb.h>
158c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
168c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
178c2ecf20Sopenharmony_ci#include <linux/mm.h>
188c2ecf20Sopenharmony_ci#include <linux/wait.h>
198c2ecf20Sopenharmony_ci#include <linux/atomic.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <media/v4l2-common.h>
228c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h>
238c2ecf20Sopenharmony_ci#include <media/v4l2-event.h>
248c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "uvcvideo.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------
298c2ecf20Sopenharmony_ci * UVC ioctls
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_cistatic int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
328c2ecf20Sopenharmony_ci	struct uvc_xu_control_mapping *xmap)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct uvc_control_mapping *map;
358c2ecf20Sopenharmony_ci	unsigned int size;
368c2ecf20Sopenharmony_ci	int ret;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	map = kzalloc(sizeof(*map), GFP_KERNEL);
398c2ecf20Sopenharmony_ci	if (map == NULL)
408c2ecf20Sopenharmony_ci		return -ENOMEM;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	map->id = xmap->id;
438c2ecf20Sopenharmony_ci	memcpy(map->name, xmap->name, sizeof(map->name));
448c2ecf20Sopenharmony_ci	memcpy(map->entity, xmap->entity, sizeof(map->entity));
458c2ecf20Sopenharmony_ci	map->selector = xmap->selector;
468c2ecf20Sopenharmony_ci	map->size = xmap->size;
478c2ecf20Sopenharmony_ci	map->offset = xmap->offset;
488c2ecf20Sopenharmony_ci	map->v4l2_type = xmap->v4l2_type;
498c2ecf20Sopenharmony_ci	map->data_type = xmap->data_type;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	switch (xmap->v4l2_type) {
528c2ecf20Sopenharmony_ci	case V4L2_CTRL_TYPE_INTEGER:
538c2ecf20Sopenharmony_ci	case V4L2_CTRL_TYPE_BOOLEAN:
548c2ecf20Sopenharmony_ci	case V4L2_CTRL_TYPE_BUTTON:
558c2ecf20Sopenharmony_ci		break;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	case V4L2_CTRL_TYPE_MENU:
588c2ecf20Sopenharmony_ci		/* Prevent excessive memory consumption, as well as integer
598c2ecf20Sopenharmony_ci		 * overflows.
608c2ecf20Sopenharmony_ci		 */
618c2ecf20Sopenharmony_ci		if (xmap->menu_count == 0 ||
628c2ecf20Sopenharmony_ci		    xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES) {
638c2ecf20Sopenharmony_ci			ret = -EINVAL;
648c2ecf20Sopenharmony_ci			goto free_map;
658c2ecf20Sopenharmony_ci		}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci		size = xmap->menu_count * sizeof(*map->menu_info);
688c2ecf20Sopenharmony_ci		map->menu_info = memdup_user(xmap->menu_info, size);
698c2ecf20Sopenharmony_ci		if (IS_ERR(map->menu_info)) {
708c2ecf20Sopenharmony_ci			ret = PTR_ERR(map->menu_info);
718c2ecf20Sopenharmony_ci			goto free_map;
728c2ecf20Sopenharmony_ci		}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci		map->menu_count = xmap->menu_count;
758c2ecf20Sopenharmony_ci		break;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	default:
788c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_CONTROL, "Unsupported V4L2 control type "
798c2ecf20Sopenharmony_ci			  "%u.\n", xmap->v4l2_type);
808c2ecf20Sopenharmony_ci		ret = -ENOTTY;
818c2ecf20Sopenharmony_ci		goto free_map;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	ret = uvc_ctrl_add_mapping(chain, map);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	kfree(map->menu_info);
878c2ecf20Sopenharmony_cifree_map:
888c2ecf20Sopenharmony_ci	kfree(map);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	return ret;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------
948c2ecf20Sopenharmony_ci * V4L2 interface
958c2ecf20Sopenharmony_ci */
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/*
988c2ecf20Sopenharmony_ci * Find the frame interval closest to the requested frame interval for the
998c2ecf20Sopenharmony_ci * given frame format and size. This should be done by the device as part of
1008c2ecf20Sopenharmony_ci * the Video Probe and Commit negotiation, but some hardware don't implement
1018c2ecf20Sopenharmony_ci * that feature.
1028c2ecf20Sopenharmony_ci */
1038c2ecf20Sopenharmony_cistatic u32 uvc_try_frame_interval(struct uvc_frame *frame, u32 interval)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	unsigned int i;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (frame->bFrameIntervalType) {
1088c2ecf20Sopenharmony_ci		u32 best = -1, dist;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci		for (i = 0; i < frame->bFrameIntervalType; ++i) {
1118c2ecf20Sopenharmony_ci			dist = interval > frame->dwFrameInterval[i]
1128c2ecf20Sopenharmony_ci			     ? interval - frame->dwFrameInterval[i]
1138c2ecf20Sopenharmony_ci			     : frame->dwFrameInterval[i] - interval;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci			if (dist > best)
1168c2ecf20Sopenharmony_ci				break;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci			best = dist;
1198c2ecf20Sopenharmony_ci		}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci		interval = frame->dwFrameInterval[i-1];
1228c2ecf20Sopenharmony_ci	} else {
1238c2ecf20Sopenharmony_ci		const u32 min = frame->dwFrameInterval[0];
1248c2ecf20Sopenharmony_ci		const u32 max = frame->dwFrameInterval[1];
1258c2ecf20Sopenharmony_ci		const u32 step = frame->dwFrameInterval[2];
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci		interval = min + (interval - min + step/2) / step * step;
1288c2ecf20Sopenharmony_ci		if (interval > max)
1298c2ecf20Sopenharmony_ci			interval = max;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return interval;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic u32 uvc_v4l2_get_bytesperline(const struct uvc_format *format,
1368c2ecf20Sopenharmony_ci	const struct uvc_frame *frame)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	switch (format->fcc) {
1398c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_NV12:
1408c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_YVU420:
1418c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_YUV420:
1428c2ecf20Sopenharmony_ci	case V4L2_PIX_FMT_M420:
1438c2ecf20Sopenharmony_ci		return frame->wWidth;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	default:
1468c2ecf20Sopenharmony_ci		return format->bpp * frame->wWidth / 8;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int uvc_v4l2_try_format(struct uvc_streaming *stream,
1518c2ecf20Sopenharmony_ci	struct v4l2_format *fmt, struct uvc_streaming_control *probe,
1528c2ecf20Sopenharmony_ci	struct uvc_format **uvc_format, struct uvc_frame **uvc_frame)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct uvc_format *format = NULL;
1558c2ecf20Sopenharmony_ci	struct uvc_frame *frame = NULL;
1568c2ecf20Sopenharmony_ci	u16 rw, rh;
1578c2ecf20Sopenharmony_ci	unsigned int d, maxd;
1588c2ecf20Sopenharmony_ci	unsigned int i;
1598c2ecf20Sopenharmony_ci	u32 interval;
1608c2ecf20Sopenharmony_ci	int ret = 0;
1618c2ecf20Sopenharmony_ci	u8 *fcc;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (fmt->type != stream->type)
1648c2ecf20Sopenharmony_ci		return -EINVAL;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	fcc = (u8 *)&fmt->fmt.pix.pixelformat;
1678c2ecf20Sopenharmony_ci	uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n",
1688c2ecf20Sopenharmony_ci			fmt->fmt.pix.pixelformat,
1698c2ecf20Sopenharmony_ci			fcc[0], fcc[1], fcc[2], fcc[3],
1708c2ecf20Sopenharmony_ci			fmt->fmt.pix.width, fmt->fmt.pix.height);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	/* Check if the hardware supports the requested format, use the default
1738c2ecf20Sopenharmony_ci	 * format otherwise.
1748c2ecf20Sopenharmony_ci	 */
1758c2ecf20Sopenharmony_ci	for (i = 0; i < stream->nformats; ++i) {
1768c2ecf20Sopenharmony_ci		format = &stream->format[i];
1778c2ecf20Sopenharmony_ci		if (format->fcc == fmt->fmt.pix.pixelformat)
1788c2ecf20Sopenharmony_ci			break;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (i == stream->nformats) {
1828c2ecf20Sopenharmony_ci		format = stream->def_format;
1838c2ecf20Sopenharmony_ci		fmt->fmt.pix.pixelformat = format->fcc;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* Find the closest image size. The distance between image sizes is
1878c2ecf20Sopenharmony_ci	 * the size in pixels of the non-overlapping regions between the
1888c2ecf20Sopenharmony_ci	 * requested size and the frame-specified size.
1898c2ecf20Sopenharmony_ci	 */
1908c2ecf20Sopenharmony_ci	rw = fmt->fmt.pix.width;
1918c2ecf20Sopenharmony_ci	rh = fmt->fmt.pix.height;
1928c2ecf20Sopenharmony_ci	maxd = (unsigned int)-1;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	for (i = 0; i < format->nframes; ++i) {
1958c2ecf20Sopenharmony_ci		u16 w = format->frame[i].wWidth;
1968c2ecf20Sopenharmony_ci		u16 h = format->frame[i].wHeight;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		d = min(w, rw) * min(h, rh);
1998c2ecf20Sopenharmony_ci		d = w*h + rw*rh - 2*d;
2008c2ecf20Sopenharmony_ci		if (d < maxd) {
2018c2ecf20Sopenharmony_ci			maxd = d;
2028c2ecf20Sopenharmony_ci			frame = &format->frame[i];
2038c2ecf20Sopenharmony_ci		}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci		if (maxd == 0)
2068c2ecf20Sopenharmony_ci			break;
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (frame == NULL) {
2108c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_FORMAT, "Unsupported size %ux%u.\n",
2118c2ecf20Sopenharmony_ci				fmt->fmt.pix.width, fmt->fmt.pix.height);
2128c2ecf20Sopenharmony_ci		return -EINVAL;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	/* Use the default frame interval. */
2168c2ecf20Sopenharmony_ci	interval = frame->dwDefaultFrameInterval;
2178c2ecf20Sopenharmony_ci	uvc_trace(UVC_TRACE_FORMAT, "Using default frame interval %u.%u us "
2188c2ecf20Sopenharmony_ci		"(%u.%u fps).\n", interval/10, interval%10, 10000000/interval,
2198c2ecf20Sopenharmony_ci		(100000000/interval)%10);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* Set the format index, frame index and frame interval. */
2228c2ecf20Sopenharmony_ci	memset(probe, 0, sizeof(*probe));
2238c2ecf20Sopenharmony_ci	probe->bmHint = 1;	/* dwFrameInterval */
2248c2ecf20Sopenharmony_ci	probe->bFormatIndex = format->index;
2258c2ecf20Sopenharmony_ci	probe->bFrameIndex = frame->bFrameIndex;
2268c2ecf20Sopenharmony_ci	probe->dwFrameInterval = uvc_try_frame_interval(frame, interval);
2278c2ecf20Sopenharmony_ci	/* Some webcams stall the probe control set request when the
2288c2ecf20Sopenharmony_ci	 * dwMaxVideoFrameSize field is set to zero. The UVC specification
2298c2ecf20Sopenharmony_ci	 * clearly states that the field is read-only from the host, so this
2308c2ecf20Sopenharmony_ci	 * is a webcam bug. Set dwMaxVideoFrameSize to the value reported by
2318c2ecf20Sopenharmony_ci	 * the webcam to work around the problem.
2328c2ecf20Sopenharmony_ci	 *
2338c2ecf20Sopenharmony_ci	 * The workaround could probably be enabled for all webcams, so the
2348c2ecf20Sopenharmony_ci	 * quirk can be removed if needed. It's currently useful to detect
2358c2ecf20Sopenharmony_ci	 * webcam bugs and fix them before they hit the market (providing
2368c2ecf20Sopenharmony_ci	 * developers test their webcams with the Linux driver as well as with
2378c2ecf20Sopenharmony_ci	 * the Windows driver).
2388c2ecf20Sopenharmony_ci	 */
2398c2ecf20Sopenharmony_ci	mutex_lock(&stream->mutex);
2408c2ecf20Sopenharmony_ci	if (stream->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS)
2418c2ecf20Sopenharmony_ci		probe->dwMaxVideoFrameSize =
2428c2ecf20Sopenharmony_ci			stream->ctrl.dwMaxVideoFrameSize;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	/* Probe the device. */
2458c2ecf20Sopenharmony_ci	ret = uvc_probe_video(stream, probe);
2468c2ecf20Sopenharmony_ci	mutex_unlock(&stream->mutex);
2478c2ecf20Sopenharmony_ci	if (ret < 0)
2488c2ecf20Sopenharmony_ci		goto done;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/* After the probe, update fmt with the values returned from
2518c2ecf20Sopenharmony_ci	 * negotiation with the device. Some devices return invalid bFormatIndex
2528c2ecf20Sopenharmony_ci	 * and bFrameIndex values, in which case we can only assume they have
2538c2ecf20Sopenharmony_ci	 * accepted the requested format as-is.
2548c2ecf20Sopenharmony_ci	 */
2558c2ecf20Sopenharmony_ci	for (i = 0; i < stream->nformats; ++i) {
2568c2ecf20Sopenharmony_ci		if (probe->bFormatIndex == stream->format[i].index) {
2578c2ecf20Sopenharmony_ci			format = &stream->format[i];
2588c2ecf20Sopenharmony_ci			break;
2598c2ecf20Sopenharmony_ci		}
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (i == stream->nformats)
2638c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_FORMAT,
2648c2ecf20Sopenharmony_ci			  "Unknown bFormatIndex %u, using default\n",
2658c2ecf20Sopenharmony_ci			  probe->bFormatIndex);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	for (i = 0; i < format->nframes; ++i) {
2688c2ecf20Sopenharmony_ci		if (probe->bFrameIndex == format->frame[i].bFrameIndex) {
2698c2ecf20Sopenharmony_ci			frame = &format->frame[i];
2708c2ecf20Sopenharmony_ci			break;
2718c2ecf20Sopenharmony_ci		}
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (i == format->nframes)
2758c2ecf20Sopenharmony_ci		uvc_trace(UVC_TRACE_FORMAT,
2768c2ecf20Sopenharmony_ci			  "Unknown bFrameIndex %u, using default\n",
2778c2ecf20Sopenharmony_ci			  probe->bFrameIndex);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	fmt->fmt.pix.width = frame->wWidth;
2808c2ecf20Sopenharmony_ci	fmt->fmt.pix.height = frame->wHeight;
2818c2ecf20Sopenharmony_ci	fmt->fmt.pix.field = V4L2_FIELD_NONE;
2828c2ecf20Sopenharmony_ci	fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame);
2838c2ecf20Sopenharmony_ci	fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize;
2848c2ecf20Sopenharmony_ci	fmt->fmt.pix.pixelformat = format->fcc;
2858c2ecf20Sopenharmony_ci	fmt->fmt.pix.colorspace = format->colorspace;
2868c2ecf20Sopenharmony_ci	fmt->fmt.pix.xfer_func = format->xfer_func;
2878c2ecf20Sopenharmony_ci	fmt->fmt.pix.ycbcr_enc = format->ycbcr_enc;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (uvc_format != NULL)
2908c2ecf20Sopenharmony_ci		*uvc_format = format;
2918c2ecf20Sopenharmony_ci	if (uvc_frame != NULL)
2928c2ecf20Sopenharmony_ci		*uvc_frame = frame;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cidone:
2958c2ecf20Sopenharmony_ci	return ret;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic int uvc_v4l2_get_format(struct uvc_streaming *stream,
2998c2ecf20Sopenharmony_ci	struct v4l2_format *fmt)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct uvc_format *format;
3028c2ecf20Sopenharmony_ci	struct uvc_frame *frame;
3038c2ecf20Sopenharmony_ci	int ret = 0;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (fmt->type != stream->type)
3068c2ecf20Sopenharmony_ci		return -EINVAL;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	mutex_lock(&stream->mutex);
3098c2ecf20Sopenharmony_ci	format = stream->cur_format;
3108c2ecf20Sopenharmony_ci	frame = stream->cur_frame;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	if (format == NULL || frame == NULL) {
3138c2ecf20Sopenharmony_ci		ret = -EINVAL;
3148c2ecf20Sopenharmony_ci		goto done;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	fmt->fmt.pix.pixelformat = format->fcc;
3188c2ecf20Sopenharmony_ci	fmt->fmt.pix.width = frame->wWidth;
3198c2ecf20Sopenharmony_ci	fmt->fmt.pix.height = frame->wHeight;
3208c2ecf20Sopenharmony_ci	fmt->fmt.pix.field = V4L2_FIELD_NONE;
3218c2ecf20Sopenharmony_ci	fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame);
3228c2ecf20Sopenharmony_ci	fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize;
3238c2ecf20Sopenharmony_ci	fmt->fmt.pix.colorspace = format->colorspace;
3248c2ecf20Sopenharmony_ci	fmt->fmt.pix.xfer_func = format->xfer_func;
3258c2ecf20Sopenharmony_ci	fmt->fmt.pix.ycbcr_enc = format->ycbcr_enc;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cidone:
3288c2ecf20Sopenharmony_ci	mutex_unlock(&stream->mutex);
3298c2ecf20Sopenharmony_ci	return ret;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic int uvc_v4l2_set_format(struct uvc_streaming *stream,
3338c2ecf20Sopenharmony_ci	struct v4l2_format *fmt)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	struct uvc_streaming_control probe;
3368c2ecf20Sopenharmony_ci	struct uvc_format *format;
3378c2ecf20Sopenharmony_ci	struct uvc_frame *frame;
3388c2ecf20Sopenharmony_ci	int ret;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if (fmt->type != stream->type)
3418c2ecf20Sopenharmony_ci		return -EINVAL;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	ret = uvc_v4l2_try_format(stream, fmt, &probe, &format, &frame);
3448c2ecf20Sopenharmony_ci	if (ret < 0)
3458c2ecf20Sopenharmony_ci		return ret;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	mutex_lock(&stream->mutex);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	if (uvc_queue_allocated(&stream->queue)) {
3508c2ecf20Sopenharmony_ci		ret = -EBUSY;
3518c2ecf20Sopenharmony_ci		goto done;
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	stream->ctrl = probe;
3558c2ecf20Sopenharmony_ci	stream->cur_format = format;
3568c2ecf20Sopenharmony_ci	stream->cur_frame = frame;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cidone:
3598c2ecf20Sopenharmony_ci	mutex_unlock(&stream->mutex);
3608c2ecf20Sopenharmony_ci	return ret;
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic int uvc_v4l2_get_streamparm(struct uvc_streaming *stream,
3648c2ecf20Sopenharmony_ci		struct v4l2_streamparm *parm)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	u32 numerator, denominator;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (parm->type != stream->type)
3698c2ecf20Sopenharmony_ci		return -EINVAL;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	mutex_lock(&stream->mutex);
3728c2ecf20Sopenharmony_ci	numerator = stream->ctrl.dwFrameInterval;
3738c2ecf20Sopenharmony_ci	mutex_unlock(&stream->mutex);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	denominator = 10000000;
3768c2ecf20Sopenharmony_ci	uvc_simplify_fraction(&numerator, &denominator, 8, 333);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	memset(parm, 0, sizeof(*parm));
3798c2ecf20Sopenharmony_ci	parm->type = stream->type;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
3828c2ecf20Sopenharmony_ci		parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
3838c2ecf20Sopenharmony_ci		parm->parm.capture.capturemode = 0;
3848c2ecf20Sopenharmony_ci		parm->parm.capture.timeperframe.numerator = numerator;
3858c2ecf20Sopenharmony_ci		parm->parm.capture.timeperframe.denominator = denominator;
3868c2ecf20Sopenharmony_ci		parm->parm.capture.extendedmode = 0;
3878c2ecf20Sopenharmony_ci		parm->parm.capture.readbuffers = 0;
3888c2ecf20Sopenharmony_ci	} else {
3898c2ecf20Sopenharmony_ci		parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
3908c2ecf20Sopenharmony_ci		parm->parm.output.outputmode = 0;
3918c2ecf20Sopenharmony_ci		parm->parm.output.timeperframe.numerator = numerator;
3928c2ecf20Sopenharmony_ci		parm->parm.output.timeperframe.denominator = denominator;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	return 0;
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
3998c2ecf20Sopenharmony_ci		struct v4l2_streamparm *parm)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	struct uvc_streaming_control probe;
4028c2ecf20Sopenharmony_ci	struct v4l2_fract timeperframe;
4038c2ecf20Sopenharmony_ci	struct uvc_format *format;
4048c2ecf20Sopenharmony_ci	struct uvc_frame *frame;
4058c2ecf20Sopenharmony_ci	u32 interval, maxd;
4068c2ecf20Sopenharmony_ci	unsigned int i;
4078c2ecf20Sopenharmony_ci	int ret;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	if (parm->type != stream->type)
4108c2ecf20Sopenharmony_ci		return -EINVAL;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
4138c2ecf20Sopenharmony_ci		timeperframe = parm->parm.capture.timeperframe;
4148c2ecf20Sopenharmony_ci	else
4158c2ecf20Sopenharmony_ci		timeperframe = parm->parm.output.timeperframe;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	interval = uvc_fraction_to_interval(timeperframe.numerator,
4188c2ecf20Sopenharmony_ci		timeperframe.denominator);
4198c2ecf20Sopenharmony_ci	uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n",
4208c2ecf20Sopenharmony_ci		timeperframe.numerator, timeperframe.denominator, interval);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	mutex_lock(&stream->mutex);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	if (uvc_queue_streaming(&stream->queue)) {
4258c2ecf20Sopenharmony_ci		mutex_unlock(&stream->mutex);
4268c2ecf20Sopenharmony_ci		return -EBUSY;
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	format = stream->cur_format;
4308c2ecf20Sopenharmony_ci	frame = stream->cur_frame;
4318c2ecf20Sopenharmony_ci	probe = stream->ctrl;
4328c2ecf20Sopenharmony_ci	probe.dwFrameInterval = uvc_try_frame_interval(frame, interval);
4338c2ecf20Sopenharmony_ci	maxd = abs((s32)probe.dwFrameInterval - interval);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	/* Try frames with matching size to find the best frame interval. */
4368c2ecf20Sopenharmony_ci	for (i = 0; i < format->nframes && maxd != 0; i++) {
4378c2ecf20Sopenharmony_ci		u32 d, ival;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci		if (&format->frame[i] == stream->cur_frame)
4408c2ecf20Sopenharmony_ci			continue;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci		if (format->frame[i].wWidth != stream->cur_frame->wWidth ||
4438c2ecf20Sopenharmony_ci		    format->frame[i].wHeight != stream->cur_frame->wHeight)
4448c2ecf20Sopenharmony_ci			continue;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci		ival = uvc_try_frame_interval(&format->frame[i], interval);
4478c2ecf20Sopenharmony_ci		d = abs((s32)ival - interval);
4488c2ecf20Sopenharmony_ci		if (d >= maxd)
4498c2ecf20Sopenharmony_ci			continue;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci		frame = &format->frame[i];
4528c2ecf20Sopenharmony_ci		probe.bFrameIndex = frame->bFrameIndex;
4538c2ecf20Sopenharmony_ci		probe.dwFrameInterval = ival;
4548c2ecf20Sopenharmony_ci		maxd = d;
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/* Probe the device with the new settings. */
4588c2ecf20Sopenharmony_ci	ret = uvc_probe_video(stream, &probe);
4598c2ecf20Sopenharmony_ci	if (ret < 0) {
4608c2ecf20Sopenharmony_ci		mutex_unlock(&stream->mutex);
4618c2ecf20Sopenharmony_ci		return ret;
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	stream->ctrl = probe;
4658c2ecf20Sopenharmony_ci	stream->cur_frame = frame;
4668c2ecf20Sopenharmony_ci	mutex_unlock(&stream->mutex);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	/* Return the actual frame period. */
4698c2ecf20Sopenharmony_ci	timeperframe.numerator = probe.dwFrameInterval;
4708c2ecf20Sopenharmony_ci	timeperframe.denominator = 10000000;
4718c2ecf20Sopenharmony_ci	uvc_simplify_fraction(&timeperframe.numerator,
4728c2ecf20Sopenharmony_ci		&timeperframe.denominator, 8, 333);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
4758c2ecf20Sopenharmony_ci		parm->parm.capture.timeperframe = timeperframe;
4768c2ecf20Sopenharmony_ci		parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
4778c2ecf20Sopenharmony_ci	} else {
4788c2ecf20Sopenharmony_ci		parm->parm.output.timeperframe = timeperframe;
4798c2ecf20Sopenharmony_ci		parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	return 0;
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------
4868c2ecf20Sopenharmony_ci * Privilege management
4878c2ecf20Sopenharmony_ci */
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci/*
4908c2ecf20Sopenharmony_ci * Privilege management is the multiple-open implementation basis. The current
4918c2ecf20Sopenharmony_ci * implementation is completely transparent for the end-user and doesn't
4928c2ecf20Sopenharmony_ci * require explicit use of the VIDIOC_G_PRIORITY and VIDIOC_S_PRIORITY ioctls.
4938c2ecf20Sopenharmony_ci * Those ioctls enable finer control on the device (by making possible for a
4948c2ecf20Sopenharmony_ci * user to request exclusive access to a device), but are not mature yet.
4958c2ecf20Sopenharmony_ci * Switching to the V4L2 priority mechanism might be considered in the future
4968c2ecf20Sopenharmony_ci * if this situation changes.
4978c2ecf20Sopenharmony_ci *
4988c2ecf20Sopenharmony_ci * Each open instance of a UVC device can either be in a privileged or
4998c2ecf20Sopenharmony_ci * unprivileged state. Only a single instance can be in a privileged state at
5008c2ecf20Sopenharmony_ci * a given time. Trying to perform an operation that requires privileges will
5018c2ecf20Sopenharmony_ci * automatically acquire the required privileges if possible, or return -EBUSY
5028c2ecf20Sopenharmony_ci * otherwise. Privileges are dismissed when closing the instance or when
5038c2ecf20Sopenharmony_ci * freeing the video buffers using VIDIOC_REQBUFS.
5048c2ecf20Sopenharmony_ci *
5058c2ecf20Sopenharmony_ci * Operations that require privileges are:
5068c2ecf20Sopenharmony_ci *
5078c2ecf20Sopenharmony_ci * - VIDIOC_S_INPUT
5088c2ecf20Sopenharmony_ci * - VIDIOC_S_PARM
5098c2ecf20Sopenharmony_ci * - VIDIOC_S_FMT
5108c2ecf20Sopenharmony_ci * - VIDIOC_REQBUFS
5118c2ecf20Sopenharmony_ci */
5128c2ecf20Sopenharmony_cistatic int uvc_acquire_privileges(struct uvc_fh *handle)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	/* Always succeed if the handle is already privileged. */
5158c2ecf20Sopenharmony_ci	if (handle->state == UVC_HANDLE_ACTIVE)
5168c2ecf20Sopenharmony_ci		return 0;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	/* Check if the device already has a privileged handle. */
5198c2ecf20Sopenharmony_ci	if (atomic_inc_return(&handle->stream->active) != 1) {
5208c2ecf20Sopenharmony_ci		atomic_dec(&handle->stream->active);
5218c2ecf20Sopenharmony_ci		return -EBUSY;
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	handle->state = UVC_HANDLE_ACTIVE;
5258c2ecf20Sopenharmony_ci	return 0;
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_cistatic void uvc_dismiss_privileges(struct uvc_fh *handle)
5298c2ecf20Sopenharmony_ci{
5308c2ecf20Sopenharmony_ci	if (handle->state == UVC_HANDLE_ACTIVE)
5318c2ecf20Sopenharmony_ci		atomic_dec(&handle->stream->active);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	handle->state = UVC_HANDLE_PASSIVE;
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic int uvc_has_privileges(struct uvc_fh *handle)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	return handle->state == UVC_HANDLE_ACTIVE;
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------
5428c2ecf20Sopenharmony_ci * V4L2 file operations
5438c2ecf20Sopenharmony_ci */
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic int uvc_v4l2_open(struct file *file)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	struct uvc_streaming *stream;
5488c2ecf20Sopenharmony_ci	struct uvc_fh *handle;
5498c2ecf20Sopenharmony_ci	int ret = 0;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n");
5528c2ecf20Sopenharmony_ci	stream = video_drvdata(file);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	ret = usb_autopm_get_interface(stream->dev->intf);
5558c2ecf20Sopenharmony_ci	if (ret < 0)
5568c2ecf20Sopenharmony_ci		return ret;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	/* Create the device handle. */
5598c2ecf20Sopenharmony_ci	handle = kzalloc(sizeof(*handle), GFP_KERNEL);
5608c2ecf20Sopenharmony_ci	if (handle == NULL) {
5618c2ecf20Sopenharmony_ci		usb_autopm_put_interface(stream->dev->intf);
5628c2ecf20Sopenharmony_ci		return -ENOMEM;
5638c2ecf20Sopenharmony_ci	}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	mutex_lock(&stream->dev->lock);
5668c2ecf20Sopenharmony_ci	if (stream->dev->users == 0) {
5678c2ecf20Sopenharmony_ci		ret = uvc_status_start(stream->dev, GFP_KERNEL);
5688c2ecf20Sopenharmony_ci		if (ret < 0) {
5698c2ecf20Sopenharmony_ci			mutex_unlock(&stream->dev->lock);
5708c2ecf20Sopenharmony_ci			usb_autopm_put_interface(stream->dev->intf);
5718c2ecf20Sopenharmony_ci			kfree(handle);
5728c2ecf20Sopenharmony_ci			return ret;
5738c2ecf20Sopenharmony_ci		}
5748c2ecf20Sopenharmony_ci	}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	stream->dev->users++;
5778c2ecf20Sopenharmony_ci	mutex_unlock(&stream->dev->lock);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	v4l2_fh_init(&handle->vfh, &stream->vdev);
5808c2ecf20Sopenharmony_ci	v4l2_fh_add(&handle->vfh);
5818c2ecf20Sopenharmony_ci	handle->chain = stream->chain;
5828c2ecf20Sopenharmony_ci	handle->stream = stream;
5838c2ecf20Sopenharmony_ci	handle->state = UVC_HANDLE_PASSIVE;
5848c2ecf20Sopenharmony_ci	file->private_data = handle;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	return 0;
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistatic int uvc_v4l2_release(struct file *file)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	struct uvc_fh *handle = file->private_data;
5928c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n");
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	/* Only free resources if this is a privileged handle. */
5978c2ecf20Sopenharmony_ci	if (uvc_has_privileges(handle))
5988c2ecf20Sopenharmony_ci		uvc_queue_release(&stream->queue);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	/* Release the file handle. */
6018c2ecf20Sopenharmony_ci	uvc_dismiss_privileges(handle);
6028c2ecf20Sopenharmony_ci	v4l2_fh_del(&handle->vfh);
6038c2ecf20Sopenharmony_ci	v4l2_fh_exit(&handle->vfh);
6048c2ecf20Sopenharmony_ci	kfree(handle);
6058c2ecf20Sopenharmony_ci	file->private_data = NULL;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	mutex_lock(&stream->dev->lock);
6088c2ecf20Sopenharmony_ci	if (--stream->dev->users == 0)
6098c2ecf20Sopenharmony_ci		uvc_status_stop(stream->dev);
6108c2ecf20Sopenharmony_ci	mutex_unlock(&stream->dev->lock);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	usb_autopm_put_interface(stream->dev->intf);
6138c2ecf20Sopenharmony_ci	return 0;
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic int uvc_ioctl_querycap(struct file *file, void *fh,
6178c2ecf20Sopenharmony_ci			      struct v4l2_capability *cap)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
6208c2ecf20Sopenharmony_ci	struct uvc_fh *handle = file->private_data;
6218c2ecf20Sopenharmony_ci	struct uvc_video_chain *chain = handle->chain;
6228c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	strscpy(cap->driver, "uvcvideo", sizeof(cap->driver));
6258c2ecf20Sopenharmony_ci	strscpy(cap->card, vdev->name, sizeof(cap->card));
6268c2ecf20Sopenharmony_ci	usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info));
6278c2ecf20Sopenharmony_ci	cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
6288c2ecf20Sopenharmony_ci			  | chain->caps;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	return 0;
6318c2ecf20Sopenharmony_ci}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_cistatic int uvc_ioctl_enum_fmt(struct uvc_streaming *stream,
6348c2ecf20Sopenharmony_ci			      struct v4l2_fmtdesc *fmt)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	struct uvc_format *format;
6378c2ecf20Sopenharmony_ci	enum v4l2_buf_type type = fmt->type;
6388c2ecf20Sopenharmony_ci	u32 index = fmt->index;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	if (fmt->type != stream->type || fmt->index >= stream->nformats)
6418c2ecf20Sopenharmony_ci		return -EINVAL;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	memset(fmt, 0, sizeof(*fmt));
6448c2ecf20Sopenharmony_ci	fmt->index = index;
6458c2ecf20Sopenharmony_ci	fmt->type = type;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	format = &stream->format[fmt->index];
6488c2ecf20Sopenharmony_ci	fmt->flags = 0;
6498c2ecf20Sopenharmony_ci	if (format->flags & UVC_FMT_FLAG_COMPRESSED)
6508c2ecf20Sopenharmony_ci		fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
6518c2ecf20Sopenharmony_ci	strscpy(fmt->description, format->name, sizeof(fmt->description));
6528c2ecf20Sopenharmony_ci	fmt->description[sizeof(fmt->description) - 1] = 0;
6538c2ecf20Sopenharmony_ci	fmt->pixelformat = format->fcc;
6548c2ecf20Sopenharmony_ci	return 0;
6558c2ecf20Sopenharmony_ci}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_cistatic int uvc_ioctl_enum_fmt_vid_cap(struct file *file, void *fh,
6588c2ecf20Sopenharmony_ci				      struct v4l2_fmtdesc *fmt)
6598c2ecf20Sopenharmony_ci{
6608c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
6618c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	return uvc_ioctl_enum_fmt(stream, fmt);
6648c2ecf20Sopenharmony_ci}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_cistatic int uvc_ioctl_enum_fmt_vid_out(struct file *file, void *fh,
6678c2ecf20Sopenharmony_ci				      struct v4l2_fmtdesc *fmt)
6688c2ecf20Sopenharmony_ci{
6698c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
6708c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	return uvc_ioctl_enum_fmt(stream, fmt);
6738c2ecf20Sopenharmony_ci}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_cistatic int uvc_ioctl_g_fmt_vid_cap(struct file *file, void *fh,
6768c2ecf20Sopenharmony_ci				   struct v4l2_format *fmt)
6778c2ecf20Sopenharmony_ci{
6788c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
6798c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	return uvc_v4l2_get_format(stream, fmt);
6828c2ecf20Sopenharmony_ci}
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_cistatic int uvc_ioctl_g_fmt_vid_out(struct file *file, void *fh,
6858c2ecf20Sopenharmony_ci				   struct v4l2_format *fmt)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
6888c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	return uvc_v4l2_get_format(stream, fmt);
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_cistatic int uvc_ioctl_s_fmt_vid_cap(struct file *file, void *fh,
6948c2ecf20Sopenharmony_ci				   struct v4l2_format *fmt)
6958c2ecf20Sopenharmony_ci{
6968c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
6978c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
6988c2ecf20Sopenharmony_ci	int ret;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	ret = uvc_acquire_privileges(handle);
7018c2ecf20Sopenharmony_ci	if (ret < 0)
7028c2ecf20Sopenharmony_ci		return ret;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	return uvc_v4l2_set_format(stream, fmt);
7058c2ecf20Sopenharmony_ci}
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_cistatic int uvc_ioctl_s_fmt_vid_out(struct file *file, void *fh,
7088c2ecf20Sopenharmony_ci				   struct v4l2_format *fmt)
7098c2ecf20Sopenharmony_ci{
7108c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
7118c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
7128c2ecf20Sopenharmony_ci	int ret;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	ret = uvc_acquire_privileges(handle);
7158c2ecf20Sopenharmony_ci	if (ret < 0)
7168c2ecf20Sopenharmony_ci		return ret;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	return uvc_v4l2_set_format(stream, fmt);
7198c2ecf20Sopenharmony_ci}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_cistatic int uvc_ioctl_try_fmt_vid_cap(struct file *file, void *fh,
7228c2ecf20Sopenharmony_ci				     struct v4l2_format *fmt)
7238c2ecf20Sopenharmony_ci{
7248c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
7258c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
7268c2ecf20Sopenharmony_ci	struct uvc_streaming_control probe;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL);
7298c2ecf20Sopenharmony_ci}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_cistatic int uvc_ioctl_try_fmt_vid_out(struct file *file, void *fh,
7328c2ecf20Sopenharmony_ci				     struct v4l2_format *fmt)
7338c2ecf20Sopenharmony_ci{
7348c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
7358c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
7368c2ecf20Sopenharmony_ci	struct uvc_streaming_control probe;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL);
7398c2ecf20Sopenharmony_ci}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_cistatic int uvc_ioctl_reqbufs(struct file *file, void *fh,
7428c2ecf20Sopenharmony_ci			     struct v4l2_requestbuffers *rb)
7438c2ecf20Sopenharmony_ci{
7448c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
7458c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
7468c2ecf20Sopenharmony_ci	int ret;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	ret = uvc_acquire_privileges(handle);
7498c2ecf20Sopenharmony_ci	if (ret < 0)
7508c2ecf20Sopenharmony_ci		return ret;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	mutex_lock(&stream->mutex);
7538c2ecf20Sopenharmony_ci	ret = uvc_request_buffers(&stream->queue, rb);
7548c2ecf20Sopenharmony_ci	mutex_unlock(&stream->mutex);
7558c2ecf20Sopenharmony_ci	if (ret < 0)
7568c2ecf20Sopenharmony_ci		return ret;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	if (ret == 0)
7598c2ecf20Sopenharmony_ci		uvc_dismiss_privileges(handle);
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	return 0;
7628c2ecf20Sopenharmony_ci}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_cistatic int uvc_ioctl_querybuf(struct file *file, void *fh,
7658c2ecf20Sopenharmony_ci			      struct v4l2_buffer *buf)
7668c2ecf20Sopenharmony_ci{
7678c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
7688c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	if (!uvc_has_privileges(handle))
7718c2ecf20Sopenharmony_ci		return -EBUSY;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	return uvc_query_buffer(&stream->queue, buf);
7748c2ecf20Sopenharmony_ci}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_cistatic int uvc_ioctl_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
7778c2ecf20Sopenharmony_ci{
7788c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
7798c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	if (!uvc_has_privileges(handle))
7828c2ecf20Sopenharmony_ci		return -EBUSY;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	return uvc_queue_buffer(&stream->queue,
7858c2ecf20Sopenharmony_ci				stream->vdev.v4l2_dev->mdev, buf);
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_cistatic int uvc_ioctl_expbuf(struct file *file, void *fh,
7898c2ecf20Sopenharmony_ci			    struct v4l2_exportbuffer *exp)
7908c2ecf20Sopenharmony_ci{
7918c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
7928c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	if (!uvc_has_privileges(handle))
7958c2ecf20Sopenharmony_ci		return -EBUSY;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	return uvc_export_buffer(&stream->queue, exp);
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_cistatic int uvc_ioctl_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
8018c2ecf20Sopenharmony_ci{
8028c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
8038c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	if (!uvc_has_privileges(handle))
8068c2ecf20Sopenharmony_ci		return -EBUSY;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	return uvc_dequeue_buffer(&stream->queue, buf,
8098c2ecf20Sopenharmony_ci				  file->f_flags & O_NONBLOCK);
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_cistatic int uvc_ioctl_create_bufs(struct file *file, void *fh,
8138c2ecf20Sopenharmony_ci				  struct v4l2_create_buffers *cb)
8148c2ecf20Sopenharmony_ci{
8158c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
8168c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
8178c2ecf20Sopenharmony_ci	int ret;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	ret = uvc_acquire_privileges(handle);
8208c2ecf20Sopenharmony_ci	if (ret < 0)
8218c2ecf20Sopenharmony_ci		return ret;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	return uvc_create_buffers(&stream->queue, cb);
8248c2ecf20Sopenharmony_ci}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_cistatic int uvc_ioctl_streamon(struct file *file, void *fh,
8278c2ecf20Sopenharmony_ci			      enum v4l2_buf_type type)
8288c2ecf20Sopenharmony_ci{
8298c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
8308c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
8318c2ecf20Sopenharmony_ci	int ret;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	if (!uvc_has_privileges(handle))
8348c2ecf20Sopenharmony_ci		return -EBUSY;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	mutex_lock(&stream->mutex);
8378c2ecf20Sopenharmony_ci	ret = uvc_queue_streamon(&stream->queue, type);
8388c2ecf20Sopenharmony_ci	mutex_unlock(&stream->mutex);
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	return ret;
8418c2ecf20Sopenharmony_ci}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_cistatic int uvc_ioctl_streamoff(struct file *file, void *fh,
8448c2ecf20Sopenharmony_ci			       enum v4l2_buf_type type)
8458c2ecf20Sopenharmony_ci{
8468c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
8478c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	if (!uvc_has_privileges(handle))
8508c2ecf20Sopenharmony_ci		return -EBUSY;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	mutex_lock(&stream->mutex);
8538c2ecf20Sopenharmony_ci	uvc_queue_streamoff(&stream->queue, type);
8548c2ecf20Sopenharmony_ci	mutex_unlock(&stream->mutex);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	return 0;
8578c2ecf20Sopenharmony_ci}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_cistatic int uvc_ioctl_enum_input(struct file *file, void *fh,
8608c2ecf20Sopenharmony_ci				struct v4l2_input *input)
8618c2ecf20Sopenharmony_ci{
8628c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
8638c2ecf20Sopenharmony_ci	struct uvc_video_chain *chain = handle->chain;
8648c2ecf20Sopenharmony_ci	const struct uvc_entity *selector = chain->selector;
8658c2ecf20Sopenharmony_ci	struct uvc_entity *iterm = NULL;
8668c2ecf20Sopenharmony_ci	struct uvc_entity *it;
8678c2ecf20Sopenharmony_ci	u32 index = input->index;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	if (selector == NULL ||
8708c2ecf20Sopenharmony_ci	    (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
8718c2ecf20Sopenharmony_ci		if (index != 0)
8728c2ecf20Sopenharmony_ci			return -EINVAL;
8738c2ecf20Sopenharmony_ci		list_for_each_entry(it, &chain->entities, chain) {
8748c2ecf20Sopenharmony_ci			if (UVC_ENTITY_IS_ITERM(it)) {
8758c2ecf20Sopenharmony_ci				iterm = it;
8768c2ecf20Sopenharmony_ci				break;
8778c2ecf20Sopenharmony_ci			}
8788c2ecf20Sopenharmony_ci		}
8798c2ecf20Sopenharmony_ci	} else if (index < selector->bNrInPins) {
8808c2ecf20Sopenharmony_ci		list_for_each_entry(it, &chain->entities, chain) {
8818c2ecf20Sopenharmony_ci			if (!UVC_ENTITY_IS_ITERM(it))
8828c2ecf20Sopenharmony_ci				continue;
8838c2ecf20Sopenharmony_ci			if (it->id == selector->baSourceID[index]) {
8848c2ecf20Sopenharmony_ci				iterm = it;
8858c2ecf20Sopenharmony_ci				break;
8868c2ecf20Sopenharmony_ci			}
8878c2ecf20Sopenharmony_ci		}
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	if (iterm == NULL)
8918c2ecf20Sopenharmony_ci		return -EINVAL;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	memset(input, 0, sizeof(*input));
8948c2ecf20Sopenharmony_ci	input->index = index;
8958c2ecf20Sopenharmony_ci	strscpy(input->name, iterm->name, sizeof(input->name));
8968c2ecf20Sopenharmony_ci	if (UVC_ENTITY_TYPE(iterm) == UVC_ITT_CAMERA)
8978c2ecf20Sopenharmony_ci		input->type = V4L2_INPUT_TYPE_CAMERA;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	return 0;
9008c2ecf20Sopenharmony_ci}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_cistatic int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input)
9038c2ecf20Sopenharmony_ci{
9048c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
9058c2ecf20Sopenharmony_ci	struct uvc_video_chain *chain = handle->chain;
9068c2ecf20Sopenharmony_ci	u8 *buf;
9078c2ecf20Sopenharmony_ci	int ret;
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	if (chain->selector == NULL ||
9108c2ecf20Sopenharmony_ci	    (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
9118c2ecf20Sopenharmony_ci		*input = 0;
9128c2ecf20Sopenharmony_ci		return 0;
9138c2ecf20Sopenharmony_ci	}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	buf = kmalloc(1, GFP_KERNEL);
9168c2ecf20Sopenharmony_ci	if (!buf)
9178c2ecf20Sopenharmony_ci		return -ENOMEM;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, chain->selector->id,
9208c2ecf20Sopenharmony_ci			     chain->dev->intfnum,  UVC_SU_INPUT_SELECT_CONTROL,
9218c2ecf20Sopenharmony_ci			     buf, 1);
9228c2ecf20Sopenharmony_ci	if (!ret)
9238c2ecf20Sopenharmony_ci		*input = *buf - 1;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	kfree(buf);
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	return ret;
9288c2ecf20Sopenharmony_ci}
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_cistatic int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
9338c2ecf20Sopenharmony_ci	struct uvc_video_chain *chain = handle->chain;
9348c2ecf20Sopenharmony_ci	u8 *buf;
9358c2ecf20Sopenharmony_ci	int ret;
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	ret = uvc_acquire_privileges(handle);
9388c2ecf20Sopenharmony_ci	if (ret < 0)
9398c2ecf20Sopenharmony_ci		return ret;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	if (chain->selector == NULL ||
9428c2ecf20Sopenharmony_ci	    (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
9438c2ecf20Sopenharmony_ci		if (input)
9448c2ecf20Sopenharmony_ci			return -EINVAL;
9458c2ecf20Sopenharmony_ci		return 0;
9468c2ecf20Sopenharmony_ci	}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	if (input >= chain->selector->bNrInPins)
9498c2ecf20Sopenharmony_ci		return -EINVAL;
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	buf = kmalloc(1, GFP_KERNEL);
9528c2ecf20Sopenharmony_ci	if (!buf)
9538c2ecf20Sopenharmony_ci		return -ENOMEM;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	*buf = input + 1;
9568c2ecf20Sopenharmony_ci	ret = uvc_query_ctrl(chain->dev, UVC_SET_CUR, chain->selector->id,
9578c2ecf20Sopenharmony_ci			     chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL,
9588c2ecf20Sopenharmony_ci			     buf, 1);
9598c2ecf20Sopenharmony_ci	kfree(buf);
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	return ret;
9628c2ecf20Sopenharmony_ci}
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_cistatic int uvc_ioctl_queryctrl(struct file *file, void *fh,
9658c2ecf20Sopenharmony_ci			       struct v4l2_queryctrl *qc)
9668c2ecf20Sopenharmony_ci{
9678c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
9688c2ecf20Sopenharmony_ci	struct uvc_video_chain *chain = handle->chain;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	return uvc_query_v4l2_ctrl(chain, qc);
9718c2ecf20Sopenharmony_ci}
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_cistatic int uvc_ioctl_query_ext_ctrl(struct file *file, void *fh,
9748c2ecf20Sopenharmony_ci				    struct v4l2_query_ext_ctrl *qec)
9758c2ecf20Sopenharmony_ci{
9768c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
9778c2ecf20Sopenharmony_ci	struct uvc_video_chain *chain = handle->chain;
9788c2ecf20Sopenharmony_ci	struct v4l2_queryctrl qc = { qec->id };
9798c2ecf20Sopenharmony_ci	int ret;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	ret = uvc_query_v4l2_ctrl(chain, &qc);
9828c2ecf20Sopenharmony_ci	if (ret)
9838c2ecf20Sopenharmony_ci		return ret;
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	qec->id = qc.id;
9868c2ecf20Sopenharmony_ci	qec->type = qc.type;
9878c2ecf20Sopenharmony_ci	strscpy(qec->name, qc.name, sizeof(qec->name));
9888c2ecf20Sopenharmony_ci	qec->minimum = qc.minimum;
9898c2ecf20Sopenharmony_ci	qec->maximum = qc.maximum;
9908c2ecf20Sopenharmony_ci	qec->step = qc.step;
9918c2ecf20Sopenharmony_ci	qec->default_value = qc.default_value;
9928c2ecf20Sopenharmony_ci	qec->flags = qc.flags;
9938c2ecf20Sopenharmony_ci	qec->elem_size = 4;
9948c2ecf20Sopenharmony_ci	qec->elems = 1;
9958c2ecf20Sopenharmony_ci	qec->nr_of_dims = 0;
9968c2ecf20Sopenharmony_ci	memset(qec->dims, 0, sizeof(qec->dims));
9978c2ecf20Sopenharmony_ci	memset(qec->reserved, 0, sizeof(qec->reserved));
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	return 0;
10008c2ecf20Sopenharmony_ci}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_cistatic int uvc_ioctl_g_ctrl(struct file *file, void *fh,
10038c2ecf20Sopenharmony_ci			    struct v4l2_control *ctrl)
10048c2ecf20Sopenharmony_ci{
10058c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
10068c2ecf20Sopenharmony_ci	struct uvc_video_chain *chain = handle->chain;
10078c2ecf20Sopenharmony_ci	struct v4l2_ext_control xctrl;
10088c2ecf20Sopenharmony_ci	int ret;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	memset(&xctrl, 0, sizeof(xctrl));
10118c2ecf20Sopenharmony_ci	xctrl.id = ctrl->id;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	ret = uvc_ctrl_begin(chain);
10148c2ecf20Sopenharmony_ci	if (ret < 0)
10158c2ecf20Sopenharmony_ci		return ret;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	ret = uvc_ctrl_get(chain, &xctrl);
10188c2ecf20Sopenharmony_ci	uvc_ctrl_rollback(handle);
10198c2ecf20Sopenharmony_ci	if (ret < 0)
10208c2ecf20Sopenharmony_ci		return ret;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	ctrl->value = xctrl.value;
10238c2ecf20Sopenharmony_ci	return 0;
10248c2ecf20Sopenharmony_ci}
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_cistatic int uvc_ioctl_s_ctrl(struct file *file, void *fh,
10278c2ecf20Sopenharmony_ci			    struct v4l2_control *ctrl)
10288c2ecf20Sopenharmony_ci{
10298c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
10308c2ecf20Sopenharmony_ci	struct uvc_video_chain *chain = handle->chain;
10318c2ecf20Sopenharmony_ci	struct v4l2_ext_control xctrl;
10328c2ecf20Sopenharmony_ci	int ret;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	memset(&xctrl, 0, sizeof(xctrl));
10358c2ecf20Sopenharmony_ci	xctrl.id = ctrl->id;
10368c2ecf20Sopenharmony_ci	xctrl.value = ctrl->value;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	ret = uvc_ctrl_begin(chain);
10398c2ecf20Sopenharmony_ci	if (ret < 0)
10408c2ecf20Sopenharmony_ci		return ret;
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	ret = uvc_ctrl_set(handle, &xctrl);
10438c2ecf20Sopenharmony_ci	if (ret < 0) {
10448c2ecf20Sopenharmony_ci		uvc_ctrl_rollback(handle);
10458c2ecf20Sopenharmony_ci		return ret;
10468c2ecf20Sopenharmony_ci	}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	ret = uvc_ctrl_commit(handle, &xctrl, 1);
10498c2ecf20Sopenharmony_ci	if (ret < 0)
10508c2ecf20Sopenharmony_ci		return ret;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	ctrl->value = xctrl.value;
10538c2ecf20Sopenharmony_ci	return 0;
10548c2ecf20Sopenharmony_ci}
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_cistatic int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
10578c2ecf20Sopenharmony_ci				 struct v4l2_ext_controls *ctrls)
10588c2ecf20Sopenharmony_ci{
10598c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
10608c2ecf20Sopenharmony_ci	struct uvc_video_chain *chain = handle->chain;
10618c2ecf20Sopenharmony_ci	struct v4l2_ext_control *ctrl = ctrls->controls;
10628c2ecf20Sopenharmony_ci	unsigned int i;
10638c2ecf20Sopenharmony_ci	int ret;
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) {
10668c2ecf20Sopenharmony_ci		for (i = 0; i < ctrls->count; ++ctrl, ++i) {
10678c2ecf20Sopenharmony_ci			struct v4l2_queryctrl qc = { .id = ctrl->id };
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci			ret = uvc_query_v4l2_ctrl(chain, &qc);
10708c2ecf20Sopenharmony_ci			if (ret < 0) {
10718c2ecf20Sopenharmony_ci				ctrls->error_idx = i;
10728c2ecf20Sopenharmony_ci				return ret;
10738c2ecf20Sopenharmony_ci			}
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci			ctrl->value = qc.default_value;
10768c2ecf20Sopenharmony_ci		}
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci		return 0;
10798c2ecf20Sopenharmony_ci	}
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	ret = uvc_ctrl_begin(chain);
10828c2ecf20Sopenharmony_ci	if (ret < 0)
10838c2ecf20Sopenharmony_ci		return ret;
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	for (i = 0; i < ctrls->count; ++ctrl, ++i) {
10868c2ecf20Sopenharmony_ci		ret = uvc_ctrl_get(chain, ctrl);
10878c2ecf20Sopenharmony_ci		if (ret < 0) {
10888c2ecf20Sopenharmony_ci			uvc_ctrl_rollback(handle);
10898c2ecf20Sopenharmony_ci			ctrls->error_idx = i;
10908c2ecf20Sopenharmony_ci			return ret;
10918c2ecf20Sopenharmony_ci		}
10928c2ecf20Sopenharmony_ci	}
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	ctrls->error_idx = 0;
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	return uvc_ctrl_rollback(handle);
10978c2ecf20Sopenharmony_ci}
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_cistatic int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle,
11008c2ecf20Sopenharmony_ci				     struct v4l2_ext_controls *ctrls,
11018c2ecf20Sopenharmony_ci				     bool commit)
11028c2ecf20Sopenharmony_ci{
11038c2ecf20Sopenharmony_ci	struct v4l2_ext_control *ctrl = ctrls->controls;
11048c2ecf20Sopenharmony_ci	struct uvc_video_chain *chain = handle->chain;
11058c2ecf20Sopenharmony_ci	unsigned int i;
11068c2ecf20Sopenharmony_ci	int ret;
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	/* Default value cannot be changed */
11098c2ecf20Sopenharmony_ci	if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL)
11108c2ecf20Sopenharmony_ci		return -EINVAL;
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	ret = uvc_ctrl_begin(chain);
11138c2ecf20Sopenharmony_ci	if (ret < 0)
11148c2ecf20Sopenharmony_ci		return ret;
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	for (i = 0; i < ctrls->count; ++ctrl, ++i) {
11178c2ecf20Sopenharmony_ci		ret = uvc_ctrl_set(handle, ctrl);
11188c2ecf20Sopenharmony_ci		if (ret < 0) {
11198c2ecf20Sopenharmony_ci			uvc_ctrl_rollback(handle);
11208c2ecf20Sopenharmony_ci			ctrls->error_idx = commit ? ctrls->count : i;
11218c2ecf20Sopenharmony_ci			return ret;
11228c2ecf20Sopenharmony_ci		}
11238c2ecf20Sopenharmony_ci	}
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	ctrls->error_idx = 0;
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	if (commit)
11288c2ecf20Sopenharmony_ci		return uvc_ctrl_commit(handle, ctrls->controls, ctrls->count);
11298c2ecf20Sopenharmony_ci	else
11308c2ecf20Sopenharmony_ci		return uvc_ctrl_rollback(handle);
11318c2ecf20Sopenharmony_ci}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_cistatic int uvc_ioctl_s_ext_ctrls(struct file *file, void *fh,
11348c2ecf20Sopenharmony_ci				 struct v4l2_ext_controls *ctrls)
11358c2ecf20Sopenharmony_ci{
11368c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, true);
11398c2ecf20Sopenharmony_ci}
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_cistatic int uvc_ioctl_try_ext_ctrls(struct file *file, void *fh,
11428c2ecf20Sopenharmony_ci				   struct v4l2_ext_controls *ctrls)
11438c2ecf20Sopenharmony_ci{
11448c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, false);
11478c2ecf20Sopenharmony_ci}
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_cistatic int uvc_ioctl_querymenu(struct file *file, void *fh,
11508c2ecf20Sopenharmony_ci			       struct v4l2_querymenu *qm)
11518c2ecf20Sopenharmony_ci{
11528c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
11538c2ecf20Sopenharmony_ci	struct uvc_video_chain *chain = handle->chain;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	return uvc_query_v4l2_menu(chain, qm);
11568c2ecf20Sopenharmony_ci}
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_cistatic int uvc_ioctl_g_selection(struct file *file, void *fh,
11598c2ecf20Sopenharmony_ci				 struct v4l2_selection *sel)
11608c2ecf20Sopenharmony_ci{
11618c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
11628c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	if (sel->type != stream->type)
11658c2ecf20Sopenharmony_ci		return -EINVAL;
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	switch (sel->target) {
11688c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP_DEFAULT:
11698c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_CROP_BOUNDS:
11708c2ecf20Sopenharmony_ci		if (stream->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
11718c2ecf20Sopenharmony_ci			return -EINVAL;
11728c2ecf20Sopenharmony_ci		break;
11738c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_DEFAULT:
11748c2ecf20Sopenharmony_ci	case V4L2_SEL_TGT_COMPOSE_BOUNDS:
11758c2ecf20Sopenharmony_ci		if (stream->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
11768c2ecf20Sopenharmony_ci			return -EINVAL;
11778c2ecf20Sopenharmony_ci		break;
11788c2ecf20Sopenharmony_ci	default:
11798c2ecf20Sopenharmony_ci		return -EINVAL;
11808c2ecf20Sopenharmony_ci	}
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	sel->r.left = 0;
11838c2ecf20Sopenharmony_ci	sel->r.top = 0;
11848c2ecf20Sopenharmony_ci	mutex_lock(&stream->mutex);
11858c2ecf20Sopenharmony_ci	sel->r.width = stream->cur_frame->wWidth;
11868c2ecf20Sopenharmony_ci	sel->r.height = stream->cur_frame->wHeight;
11878c2ecf20Sopenharmony_ci	mutex_unlock(&stream->mutex);
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	return 0;
11908c2ecf20Sopenharmony_ci}
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_cistatic int uvc_ioctl_g_parm(struct file *file, void *fh,
11938c2ecf20Sopenharmony_ci			    struct v4l2_streamparm *parm)
11948c2ecf20Sopenharmony_ci{
11958c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
11968c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	return uvc_v4l2_get_streamparm(stream, parm);
11998c2ecf20Sopenharmony_ci}
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_cistatic int uvc_ioctl_s_parm(struct file *file, void *fh,
12028c2ecf20Sopenharmony_ci			    struct v4l2_streamparm *parm)
12038c2ecf20Sopenharmony_ci{
12048c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
12058c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
12068c2ecf20Sopenharmony_ci	int ret;
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	ret = uvc_acquire_privileges(handle);
12098c2ecf20Sopenharmony_ci	if (ret < 0)
12108c2ecf20Sopenharmony_ci		return ret;
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci	return uvc_v4l2_set_streamparm(stream, parm);
12138c2ecf20Sopenharmony_ci}
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_cistatic int uvc_ioctl_enum_framesizes(struct file *file, void *fh,
12168c2ecf20Sopenharmony_ci				     struct v4l2_frmsizeenum *fsize)
12178c2ecf20Sopenharmony_ci{
12188c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
12198c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
12208c2ecf20Sopenharmony_ci	struct uvc_format *format = NULL;
12218c2ecf20Sopenharmony_ci	struct uvc_frame *frame = NULL;
12228c2ecf20Sopenharmony_ci	unsigned int index;
12238c2ecf20Sopenharmony_ci	unsigned int i;
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	/* Look for the given pixel format */
12268c2ecf20Sopenharmony_ci	for (i = 0; i < stream->nformats; i++) {
12278c2ecf20Sopenharmony_ci		if (stream->format[i].fcc == fsize->pixel_format) {
12288c2ecf20Sopenharmony_ci			format = &stream->format[i];
12298c2ecf20Sopenharmony_ci			break;
12308c2ecf20Sopenharmony_ci		}
12318c2ecf20Sopenharmony_ci	}
12328c2ecf20Sopenharmony_ci	if (format == NULL)
12338c2ecf20Sopenharmony_ci		return -EINVAL;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	/* Skip duplicate frame sizes */
12368c2ecf20Sopenharmony_ci	for (i = 0, index = 0; i < format->nframes; i++) {
12378c2ecf20Sopenharmony_ci		if (frame && frame->wWidth == format->frame[i].wWidth &&
12388c2ecf20Sopenharmony_ci		    frame->wHeight == format->frame[i].wHeight)
12398c2ecf20Sopenharmony_ci			continue;
12408c2ecf20Sopenharmony_ci		frame = &format->frame[i];
12418c2ecf20Sopenharmony_ci		if (index == fsize->index)
12428c2ecf20Sopenharmony_ci			break;
12438c2ecf20Sopenharmony_ci		index++;
12448c2ecf20Sopenharmony_ci	}
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	if (i == format->nframes)
12478c2ecf20Sopenharmony_ci		return -EINVAL;
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
12508c2ecf20Sopenharmony_ci	fsize->discrete.width = frame->wWidth;
12518c2ecf20Sopenharmony_ci	fsize->discrete.height = frame->wHeight;
12528c2ecf20Sopenharmony_ci	return 0;
12538c2ecf20Sopenharmony_ci}
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_cistatic int uvc_ioctl_enum_frameintervals(struct file *file, void *fh,
12568c2ecf20Sopenharmony_ci					 struct v4l2_frmivalenum *fival)
12578c2ecf20Sopenharmony_ci{
12588c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
12598c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
12608c2ecf20Sopenharmony_ci	struct uvc_format *format = NULL;
12618c2ecf20Sopenharmony_ci	struct uvc_frame *frame = NULL;
12628c2ecf20Sopenharmony_ci	unsigned int nintervals;
12638c2ecf20Sopenharmony_ci	unsigned int index;
12648c2ecf20Sopenharmony_ci	unsigned int i;
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	/* Look for the given pixel format and frame size */
12678c2ecf20Sopenharmony_ci	for (i = 0; i < stream->nformats; i++) {
12688c2ecf20Sopenharmony_ci		if (stream->format[i].fcc == fival->pixel_format) {
12698c2ecf20Sopenharmony_ci			format = &stream->format[i];
12708c2ecf20Sopenharmony_ci			break;
12718c2ecf20Sopenharmony_ci		}
12728c2ecf20Sopenharmony_ci	}
12738c2ecf20Sopenharmony_ci	if (format == NULL)
12748c2ecf20Sopenharmony_ci		return -EINVAL;
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	index = fival->index;
12778c2ecf20Sopenharmony_ci	for (i = 0; i < format->nframes; i++) {
12788c2ecf20Sopenharmony_ci		if (format->frame[i].wWidth == fival->width &&
12798c2ecf20Sopenharmony_ci		    format->frame[i].wHeight == fival->height) {
12808c2ecf20Sopenharmony_ci			frame = &format->frame[i];
12818c2ecf20Sopenharmony_ci			nintervals = frame->bFrameIntervalType ?: 1;
12828c2ecf20Sopenharmony_ci			if (index < nintervals)
12838c2ecf20Sopenharmony_ci				break;
12848c2ecf20Sopenharmony_ci			index -= nintervals;
12858c2ecf20Sopenharmony_ci		}
12868c2ecf20Sopenharmony_ci	}
12878c2ecf20Sopenharmony_ci	if (i == format->nframes)
12888c2ecf20Sopenharmony_ci		return -EINVAL;
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	if (frame->bFrameIntervalType) {
12918c2ecf20Sopenharmony_ci		fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
12928c2ecf20Sopenharmony_ci		fival->discrete.numerator =
12938c2ecf20Sopenharmony_ci			frame->dwFrameInterval[index];
12948c2ecf20Sopenharmony_ci		fival->discrete.denominator = 10000000;
12958c2ecf20Sopenharmony_ci		uvc_simplify_fraction(&fival->discrete.numerator,
12968c2ecf20Sopenharmony_ci			&fival->discrete.denominator, 8, 333);
12978c2ecf20Sopenharmony_ci	} else {
12988c2ecf20Sopenharmony_ci		fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
12998c2ecf20Sopenharmony_ci		fival->stepwise.min.numerator = frame->dwFrameInterval[0];
13008c2ecf20Sopenharmony_ci		fival->stepwise.min.denominator = 10000000;
13018c2ecf20Sopenharmony_ci		fival->stepwise.max.numerator = frame->dwFrameInterval[1];
13028c2ecf20Sopenharmony_ci		fival->stepwise.max.denominator = 10000000;
13038c2ecf20Sopenharmony_ci		fival->stepwise.step.numerator = frame->dwFrameInterval[2];
13048c2ecf20Sopenharmony_ci		fival->stepwise.step.denominator = 10000000;
13058c2ecf20Sopenharmony_ci		uvc_simplify_fraction(&fival->stepwise.min.numerator,
13068c2ecf20Sopenharmony_ci			&fival->stepwise.min.denominator, 8, 333);
13078c2ecf20Sopenharmony_ci		uvc_simplify_fraction(&fival->stepwise.max.numerator,
13088c2ecf20Sopenharmony_ci			&fival->stepwise.max.denominator, 8, 333);
13098c2ecf20Sopenharmony_ci		uvc_simplify_fraction(&fival->stepwise.step.numerator,
13108c2ecf20Sopenharmony_ci			&fival->stepwise.step.denominator, 8, 333);
13118c2ecf20Sopenharmony_ci	}
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	return 0;
13148c2ecf20Sopenharmony_ci}
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_cistatic int uvc_ioctl_subscribe_event(struct v4l2_fh *fh,
13178c2ecf20Sopenharmony_ci				     const struct v4l2_event_subscription *sub)
13188c2ecf20Sopenharmony_ci{
13198c2ecf20Sopenharmony_ci	switch (sub->type) {
13208c2ecf20Sopenharmony_ci	case V4L2_EVENT_CTRL:
13218c2ecf20Sopenharmony_ci		return v4l2_event_subscribe(fh, sub, 0, &uvc_ctrl_sub_ev_ops);
13228c2ecf20Sopenharmony_ci	default:
13238c2ecf20Sopenharmony_ci		return -EINVAL;
13248c2ecf20Sopenharmony_ci	}
13258c2ecf20Sopenharmony_ci}
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_cistatic long uvc_ioctl_default(struct file *file, void *fh, bool valid_prio,
13288c2ecf20Sopenharmony_ci			      unsigned int cmd, void *arg)
13298c2ecf20Sopenharmony_ci{
13308c2ecf20Sopenharmony_ci	struct uvc_fh *handle = fh;
13318c2ecf20Sopenharmony_ci	struct uvc_video_chain *chain = handle->chain;
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	switch (cmd) {
13348c2ecf20Sopenharmony_ci	/* Dynamic controls. */
13358c2ecf20Sopenharmony_ci	case UVCIOC_CTRL_MAP:
13368c2ecf20Sopenharmony_ci		return uvc_ioctl_ctrl_map(chain, arg);
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	case UVCIOC_CTRL_QUERY:
13398c2ecf20Sopenharmony_ci		return uvc_xu_ctrl_query(chain, arg);
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	default:
13428c2ecf20Sopenharmony_ci		return -ENOTTY;
13438c2ecf20Sopenharmony_ci	}
13448c2ecf20Sopenharmony_ci}
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
13478c2ecf20Sopenharmony_cistruct uvc_xu_control_mapping32 {
13488c2ecf20Sopenharmony_ci	u32 id;
13498c2ecf20Sopenharmony_ci	u8 name[32];
13508c2ecf20Sopenharmony_ci	u8 entity[16];
13518c2ecf20Sopenharmony_ci	u8 selector;
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	u8 size;
13548c2ecf20Sopenharmony_ci	u8 offset;
13558c2ecf20Sopenharmony_ci	u32 v4l2_type;
13568c2ecf20Sopenharmony_ci	u32 data_type;
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	compat_caddr_t menu_info;
13598c2ecf20Sopenharmony_ci	u32 menu_count;
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	u32 reserved[4];
13628c2ecf20Sopenharmony_ci};
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_cistatic int uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping *kp,
13658c2ecf20Sopenharmony_ci			const struct uvc_xu_control_mapping32 __user *up)
13668c2ecf20Sopenharmony_ci{
13678c2ecf20Sopenharmony_ci	struct uvc_xu_control_mapping32 *p = (void *)kp;
13688c2ecf20Sopenharmony_ci	compat_caddr_t info;
13698c2ecf20Sopenharmony_ci	u32 count;
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	if (copy_from_user(p, up, sizeof(*p)))
13728c2ecf20Sopenharmony_ci		return -EFAULT;
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_ci	count = p->menu_count;
13758c2ecf20Sopenharmony_ci	info = p->menu_info;
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	memset(kp->reserved, 0, sizeof(kp->reserved));
13788c2ecf20Sopenharmony_ci	kp->menu_info = count ? compat_ptr(info) : NULL;
13798c2ecf20Sopenharmony_ci	kp->menu_count = count;
13808c2ecf20Sopenharmony_ci	return 0;
13818c2ecf20Sopenharmony_ci}
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_cistatic int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp,
13848c2ecf20Sopenharmony_ci			struct uvc_xu_control_mapping32 __user *up)
13858c2ecf20Sopenharmony_ci{
13868c2ecf20Sopenharmony_ci	if (copy_to_user(up, kp, offsetof(typeof(*up), menu_info)) ||
13878c2ecf20Sopenharmony_ci	    put_user(kp->menu_count, &up->menu_count))
13888c2ecf20Sopenharmony_ci		return -EFAULT;
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	if (clear_user(up->reserved, sizeof(up->reserved)))
13918c2ecf20Sopenharmony_ci		return -EFAULT;
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	return 0;
13948c2ecf20Sopenharmony_ci}
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_cistruct uvc_xu_control_query32 {
13978c2ecf20Sopenharmony_ci	u8 unit;
13988c2ecf20Sopenharmony_ci	u8 selector;
13998c2ecf20Sopenharmony_ci	u8 query;
14008c2ecf20Sopenharmony_ci	u16 size;
14018c2ecf20Sopenharmony_ci	compat_caddr_t data;
14028c2ecf20Sopenharmony_ci};
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_cistatic int uvc_v4l2_get_xu_query(struct uvc_xu_control_query *kp,
14058c2ecf20Sopenharmony_ci			const struct uvc_xu_control_query32 __user *up)
14068c2ecf20Sopenharmony_ci{
14078c2ecf20Sopenharmony_ci	struct uvc_xu_control_query32 v;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	if (copy_from_user(&v, up, sizeof(v)))
14108c2ecf20Sopenharmony_ci		return -EFAULT;
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci	*kp = (struct uvc_xu_control_query){
14138c2ecf20Sopenharmony_ci		.unit = v.unit,
14148c2ecf20Sopenharmony_ci		.selector = v.selector,
14158c2ecf20Sopenharmony_ci		.query = v.query,
14168c2ecf20Sopenharmony_ci		.size = v.size,
14178c2ecf20Sopenharmony_ci		.data = v.size ? compat_ptr(v.data) : NULL
14188c2ecf20Sopenharmony_ci	};
14198c2ecf20Sopenharmony_ci	return 0;
14208c2ecf20Sopenharmony_ci}
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_cistatic int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp,
14238c2ecf20Sopenharmony_ci			struct uvc_xu_control_query32 __user *up)
14248c2ecf20Sopenharmony_ci{
14258c2ecf20Sopenharmony_ci	if (copy_to_user(up, kp, offsetof(typeof(*up), data)))
14268c2ecf20Sopenharmony_ci		return -EFAULT;
14278c2ecf20Sopenharmony_ci	return 0;
14288c2ecf20Sopenharmony_ci}
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci#define UVCIOC_CTRL_MAP32	_IOWR('u', 0x20, struct uvc_xu_control_mapping32)
14318c2ecf20Sopenharmony_ci#define UVCIOC_CTRL_QUERY32	_IOWR('u', 0x21, struct uvc_xu_control_query32)
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_cistatic long uvc_v4l2_compat_ioctl32(struct file *file,
14348c2ecf20Sopenharmony_ci		     unsigned int cmd, unsigned long arg)
14358c2ecf20Sopenharmony_ci{
14368c2ecf20Sopenharmony_ci	struct uvc_fh *handle = file->private_data;
14378c2ecf20Sopenharmony_ci	union {
14388c2ecf20Sopenharmony_ci		struct uvc_xu_control_mapping xmap;
14398c2ecf20Sopenharmony_ci		struct uvc_xu_control_query xqry;
14408c2ecf20Sopenharmony_ci	} karg;
14418c2ecf20Sopenharmony_ci	void __user *up = compat_ptr(arg);
14428c2ecf20Sopenharmony_ci	long ret;
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci	switch (cmd) {
14458c2ecf20Sopenharmony_ci	case UVCIOC_CTRL_MAP32:
14468c2ecf20Sopenharmony_ci		ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
14478c2ecf20Sopenharmony_ci		if (ret)
14488c2ecf20Sopenharmony_ci			return ret;
14498c2ecf20Sopenharmony_ci		ret = uvc_ioctl_ctrl_map(handle->chain, &karg.xmap);
14508c2ecf20Sopenharmony_ci		if (ret)
14518c2ecf20Sopenharmony_ci			return ret;
14528c2ecf20Sopenharmony_ci		ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up);
14538c2ecf20Sopenharmony_ci		if (ret)
14548c2ecf20Sopenharmony_ci			return ret;
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci		break;
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci	case UVCIOC_CTRL_QUERY32:
14598c2ecf20Sopenharmony_ci		ret = uvc_v4l2_get_xu_query(&karg.xqry, up);
14608c2ecf20Sopenharmony_ci		if (ret)
14618c2ecf20Sopenharmony_ci			return ret;
14628c2ecf20Sopenharmony_ci		ret = uvc_xu_ctrl_query(handle->chain, &karg.xqry);
14638c2ecf20Sopenharmony_ci		if (ret)
14648c2ecf20Sopenharmony_ci			return ret;
14658c2ecf20Sopenharmony_ci		ret = uvc_v4l2_put_xu_query(&karg.xqry, up);
14668c2ecf20Sopenharmony_ci		if (ret)
14678c2ecf20Sopenharmony_ci			return ret;
14688c2ecf20Sopenharmony_ci		break;
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci	default:
14718c2ecf20Sopenharmony_ci		return -ENOIOCTLCMD;
14728c2ecf20Sopenharmony_ci	}
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci	return ret;
14758c2ecf20Sopenharmony_ci}
14768c2ecf20Sopenharmony_ci#endif
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_cistatic ssize_t uvc_v4l2_read(struct file *file, char __user *data,
14798c2ecf20Sopenharmony_ci		    size_t count, loff_t *ppos)
14808c2ecf20Sopenharmony_ci{
14818c2ecf20Sopenharmony_ci	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n");
14828c2ecf20Sopenharmony_ci	return -EINVAL;
14838c2ecf20Sopenharmony_ci}
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_cistatic int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
14868c2ecf20Sopenharmony_ci{
14878c2ecf20Sopenharmony_ci	struct uvc_fh *handle = file->private_data;
14888c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_mmap\n");
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci	return uvc_queue_mmap(&stream->queue, vma);
14938c2ecf20Sopenharmony_ci}
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_cistatic __poll_t uvc_v4l2_poll(struct file *file, poll_table *wait)
14968c2ecf20Sopenharmony_ci{
14978c2ecf20Sopenharmony_ci	struct uvc_fh *handle = file->private_data;
14988c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n");
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	return uvc_queue_poll(&stream->queue, file, wait);
15038c2ecf20Sopenharmony_ci}
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci#ifndef CONFIG_MMU
15068c2ecf20Sopenharmony_cistatic unsigned long uvc_v4l2_get_unmapped_area(struct file *file,
15078c2ecf20Sopenharmony_ci		unsigned long addr, unsigned long len, unsigned long pgoff,
15088c2ecf20Sopenharmony_ci		unsigned long flags)
15098c2ecf20Sopenharmony_ci{
15108c2ecf20Sopenharmony_ci	struct uvc_fh *handle = file->private_data;
15118c2ecf20Sopenharmony_ci	struct uvc_streaming *stream = handle->stream;
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_get_unmapped_area\n");
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	return uvc_queue_get_unmapped_area(&stream->queue, pgoff);
15168c2ecf20Sopenharmony_ci}
15178c2ecf20Sopenharmony_ci#endif
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ciconst struct v4l2_ioctl_ops uvc_ioctl_ops = {
15208c2ecf20Sopenharmony_ci	.vidioc_querycap = uvc_ioctl_querycap,
15218c2ecf20Sopenharmony_ci	.vidioc_enum_fmt_vid_cap = uvc_ioctl_enum_fmt_vid_cap,
15228c2ecf20Sopenharmony_ci	.vidioc_enum_fmt_vid_out = uvc_ioctl_enum_fmt_vid_out,
15238c2ecf20Sopenharmony_ci	.vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt_vid_cap,
15248c2ecf20Sopenharmony_ci	.vidioc_g_fmt_vid_out = uvc_ioctl_g_fmt_vid_out,
15258c2ecf20Sopenharmony_ci	.vidioc_s_fmt_vid_cap = uvc_ioctl_s_fmt_vid_cap,
15268c2ecf20Sopenharmony_ci	.vidioc_s_fmt_vid_out = uvc_ioctl_s_fmt_vid_out,
15278c2ecf20Sopenharmony_ci	.vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt_vid_cap,
15288c2ecf20Sopenharmony_ci	.vidioc_try_fmt_vid_out = uvc_ioctl_try_fmt_vid_out,
15298c2ecf20Sopenharmony_ci	.vidioc_reqbufs = uvc_ioctl_reqbufs,
15308c2ecf20Sopenharmony_ci	.vidioc_querybuf = uvc_ioctl_querybuf,
15318c2ecf20Sopenharmony_ci	.vidioc_qbuf = uvc_ioctl_qbuf,
15328c2ecf20Sopenharmony_ci	.vidioc_expbuf = uvc_ioctl_expbuf,
15338c2ecf20Sopenharmony_ci	.vidioc_dqbuf = uvc_ioctl_dqbuf,
15348c2ecf20Sopenharmony_ci	.vidioc_create_bufs = uvc_ioctl_create_bufs,
15358c2ecf20Sopenharmony_ci	.vidioc_streamon = uvc_ioctl_streamon,
15368c2ecf20Sopenharmony_ci	.vidioc_streamoff = uvc_ioctl_streamoff,
15378c2ecf20Sopenharmony_ci	.vidioc_enum_input = uvc_ioctl_enum_input,
15388c2ecf20Sopenharmony_ci	.vidioc_g_input = uvc_ioctl_g_input,
15398c2ecf20Sopenharmony_ci	.vidioc_s_input = uvc_ioctl_s_input,
15408c2ecf20Sopenharmony_ci	.vidioc_queryctrl = uvc_ioctl_queryctrl,
15418c2ecf20Sopenharmony_ci	.vidioc_query_ext_ctrl = uvc_ioctl_query_ext_ctrl,
15428c2ecf20Sopenharmony_ci	.vidioc_g_ctrl = uvc_ioctl_g_ctrl,
15438c2ecf20Sopenharmony_ci	.vidioc_s_ctrl = uvc_ioctl_s_ctrl,
15448c2ecf20Sopenharmony_ci	.vidioc_g_ext_ctrls = uvc_ioctl_g_ext_ctrls,
15458c2ecf20Sopenharmony_ci	.vidioc_s_ext_ctrls = uvc_ioctl_s_ext_ctrls,
15468c2ecf20Sopenharmony_ci	.vidioc_try_ext_ctrls = uvc_ioctl_try_ext_ctrls,
15478c2ecf20Sopenharmony_ci	.vidioc_querymenu = uvc_ioctl_querymenu,
15488c2ecf20Sopenharmony_ci	.vidioc_g_selection = uvc_ioctl_g_selection,
15498c2ecf20Sopenharmony_ci	.vidioc_g_parm = uvc_ioctl_g_parm,
15508c2ecf20Sopenharmony_ci	.vidioc_s_parm = uvc_ioctl_s_parm,
15518c2ecf20Sopenharmony_ci	.vidioc_enum_framesizes = uvc_ioctl_enum_framesizes,
15528c2ecf20Sopenharmony_ci	.vidioc_enum_frameintervals = uvc_ioctl_enum_frameintervals,
15538c2ecf20Sopenharmony_ci	.vidioc_subscribe_event = uvc_ioctl_subscribe_event,
15548c2ecf20Sopenharmony_ci	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
15558c2ecf20Sopenharmony_ci	.vidioc_default = uvc_ioctl_default,
15568c2ecf20Sopenharmony_ci};
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ciconst struct v4l2_file_operations uvc_fops = {
15598c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
15608c2ecf20Sopenharmony_ci	.open		= uvc_v4l2_open,
15618c2ecf20Sopenharmony_ci	.release	= uvc_v4l2_release,
15628c2ecf20Sopenharmony_ci	.unlocked_ioctl	= video_ioctl2,
15638c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
15648c2ecf20Sopenharmony_ci	.compat_ioctl32	= uvc_v4l2_compat_ioctl32,
15658c2ecf20Sopenharmony_ci#endif
15668c2ecf20Sopenharmony_ci	.read		= uvc_v4l2_read,
15678c2ecf20Sopenharmony_ci	.mmap		= uvc_v4l2_mmap,
15688c2ecf20Sopenharmony_ci	.poll		= uvc_v4l2_poll,
15698c2ecf20Sopenharmony_ci#ifndef CONFIG_MMU
15708c2ecf20Sopenharmony_ci	.get_unmapped_area = uvc_v4l2_get_unmapped_area,
15718c2ecf20Sopenharmony_ci#endif
15728c2ecf20Sopenharmony_ci};
15738c2ecf20Sopenharmony_ci
1574