162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	uvc_v4l2.c  --  USB Video Class Gadget driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *	Copyright (C) 2009-2010
662306a36Sopenharmony_ci *	    Laurent Pinchart (laurent.pinchart@ideasonboard.com)
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/device.h>
1062306a36Sopenharmony_ci#include <linux/errno.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/list.h>
1362306a36Sopenharmony_ci#include <linux/usb/g_uvc.h>
1462306a36Sopenharmony_ci#include <linux/usb/uvc.h>
1562306a36Sopenharmony_ci#include <linux/videodev2.h>
1662306a36Sopenharmony_ci#include <linux/vmalloc.h>
1762306a36Sopenharmony_ci#include <linux/wait.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <media/v4l2-dev.h>
2062306a36Sopenharmony_ci#include <media/v4l2-event.h>
2162306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "f_uvc.h"
2462306a36Sopenharmony_ci#include "uvc.h"
2562306a36Sopenharmony_ci#include "uvc_queue.h"
2662306a36Sopenharmony_ci#include "uvc_video.h"
2762306a36Sopenharmony_ci#include "uvc_v4l2.h"
2862306a36Sopenharmony_ci#include "uvc_configfs.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic const struct uvc_format_desc *to_uvc_format(struct uvcg_format *uformat)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	char guid[16] = UVC_GUID_FORMAT_MJPEG;
3362306a36Sopenharmony_ci	const struct uvc_format_desc *format;
3462306a36Sopenharmony_ci	struct uvcg_uncompressed *unc;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	if (uformat->type == UVCG_UNCOMPRESSED) {
3762306a36Sopenharmony_ci		unc = to_uvcg_uncompressed(&uformat->group.cg_item);
3862306a36Sopenharmony_ci		if (!unc)
3962306a36Sopenharmony_ci			return ERR_PTR(-EINVAL);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci		memcpy(guid, unc->desc.guidFormat, sizeof(guid));
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	format = uvc_format_by_guid(guid);
4562306a36Sopenharmony_ci	if (!format)
4662306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	return format;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int uvc_v4l2_get_bytesperline(struct uvcg_format *uformat,
5262306a36Sopenharmony_ci			      struct uvcg_frame *uframe)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct uvcg_uncompressed *u;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (uformat->type == UVCG_UNCOMPRESSED) {
5762306a36Sopenharmony_ci		u = to_uvcg_uncompressed(&uformat->group.cg_item);
5862306a36Sopenharmony_ci		if (!u)
5962306a36Sopenharmony_ci			return 0;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		return u->desc.bBitsPerPixel * uframe->frame.w_width / 8;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	return 0;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int uvc_get_frame_size(struct uvcg_format *uformat,
6862306a36Sopenharmony_ci		       struct uvcg_frame *uframe)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	unsigned int bpl = uvc_v4l2_get_bytesperline(uformat, uframe);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return bpl ? bpl * uframe->frame.w_height :
7362306a36Sopenharmony_ci		uframe->frame.dw_max_video_frame_buffer_size;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic struct uvcg_format *find_format_by_index(struct uvc_device *uvc, int index)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct uvcg_format_ptr *format;
7962306a36Sopenharmony_ci	struct uvcg_format *uformat = NULL;
8062306a36Sopenharmony_ci	int i = 1;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	list_for_each_entry(format, &uvc->header->formats, entry) {
8362306a36Sopenharmony_ci		if (index == i) {
8462306a36Sopenharmony_ci			uformat = format->fmt;
8562306a36Sopenharmony_ci			break;
8662306a36Sopenharmony_ci		}
8762306a36Sopenharmony_ci		i++;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return uformat;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic struct uvcg_frame *find_frame_by_index(struct uvc_device *uvc,
9462306a36Sopenharmony_ci				       struct uvcg_format *uformat,
9562306a36Sopenharmony_ci				       int index)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct uvcg_format_ptr *format;
9862306a36Sopenharmony_ci	struct uvcg_frame_ptr *frame;
9962306a36Sopenharmony_ci	struct uvcg_frame *uframe = NULL;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	list_for_each_entry(format, &uvc->header->formats, entry) {
10262306a36Sopenharmony_ci		if (format->fmt->type != uformat->type)
10362306a36Sopenharmony_ci			continue;
10462306a36Sopenharmony_ci		list_for_each_entry(frame, &format->fmt->frames, entry) {
10562306a36Sopenharmony_ci			if (index == frame->frm->frame.b_frame_index) {
10662306a36Sopenharmony_ci				uframe = frame->frm;
10762306a36Sopenharmony_ci				break;
10862306a36Sopenharmony_ci			}
10962306a36Sopenharmony_ci		}
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	return uframe;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic struct uvcg_format *find_format_by_pix(struct uvc_device *uvc,
11662306a36Sopenharmony_ci					      u32 pixelformat)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct uvcg_format_ptr *format;
11962306a36Sopenharmony_ci	struct uvcg_format *uformat = NULL;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	list_for_each_entry(format, &uvc->header->formats, entry) {
12262306a36Sopenharmony_ci		const struct uvc_format_desc *fmtdesc = to_uvc_format(format->fmt);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		if (fmtdesc->fcc == pixelformat) {
12562306a36Sopenharmony_ci			uformat = format->fmt;
12662306a36Sopenharmony_ci			break;
12762306a36Sopenharmony_ci		}
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return uformat;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic struct uvcg_frame *find_closest_frame_by_size(struct uvc_device *uvc,
13462306a36Sopenharmony_ci					   struct uvcg_format *uformat,
13562306a36Sopenharmony_ci					   u16 rw, u16 rh)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct uvc_video *video = &uvc->video;
13862306a36Sopenharmony_ci	struct uvcg_format_ptr *format;
13962306a36Sopenharmony_ci	struct uvcg_frame_ptr *frame;
14062306a36Sopenharmony_ci	struct uvcg_frame *uframe = NULL;
14162306a36Sopenharmony_ci	unsigned int d, maxd;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	/* Find the closest image size. The distance between image sizes is
14462306a36Sopenharmony_ci	 * the size in pixels of the non-overlapping regions between the
14562306a36Sopenharmony_ci	 * requested size and the frame-specified size.
14662306a36Sopenharmony_ci	 */
14762306a36Sopenharmony_ci	maxd = (unsigned int)-1;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	list_for_each_entry(format, &uvc->header->formats, entry) {
15062306a36Sopenharmony_ci		if (format->fmt->type != uformat->type)
15162306a36Sopenharmony_ci			continue;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		list_for_each_entry(frame, &format->fmt->frames, entry) {
15462306a36Sopenharmony_ci			u16 w, h;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci			w = frame->frm->frame.w_width;
15762306a36Sopenharmony_ci			h = frame->frm->frame.w_height;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci			d = min(w, rw) * min(h, rh);
16062306a36Sopenharmony_ci			d = w*h + rw*rh - 2*d;
16162306a36Sopenharmony_ci			if (d < maxd) {
16262306a36Sopenharmony_ci				maxd = d;
16362306a36Sopenharmony_ci				uframe = frame->frm;
16462306a36Sopenharmony_ci			}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci			if (maxd == 0)
16762306a36Sopenharmony_ci				break;
16862306a36Sopenharmony_ci		}
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (!uframe)
17262306a36Sopenharmony_ci		uvcg_dbg(&video->uvc->func, "Unsupported size %ux%u\n", rw, rh);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return uframe;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/* --------------------------------------------------------------------------
17862306a36Sopenharmony_ci * Requests handling
17962306a36Sopenharmony_ci */
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic int
18262306a36Sopenharmony_ciuvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	struct usb_composite_dev *cdev = uvc->func.config->cdev;
18562306a36Sopenharmony_ci	struct usb_request *req = uvc->control_req;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (data->length < 0)
18862306a36Sopenharmony_ci		return usb_ep_set_halt(cdev->gadget->ep0);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	req->length = min_t(unsigned int, uvc->event_length, data->length);
19162306a36Sopenharmony_ci	req->zero = data->length < uvc->event_length;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	memcpy(req->buf, data->data, req->length);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return usb_ep_queue(cdev->gadget->ep0, req, GFP_KERNEL);
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/* --------------------------------------------------------------------------
19962306a36Sopenharmony_ci * V4L2 ioctls
20062306a36Sopenharmony_ci */
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic int
20362306a36Sopenharmony_ciuvc_v4l2_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
20662306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
20762306a36Sopenharmony_ci	struct usb_composite_dev *cdev = uvc->func.config->cdev;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	strscpy(cap->driver, "g_uvc", sizeof(cap->driver));
21062306a36Sopenharmony_ci	strscpy(cap->card, cdev->gadget->name, sizeof(cap->card));
21162306a36Sopenharmony_ci	strscpy(cap->bus_info, dev_name(&cdev->gadget->dev),
21262306a36Sopenharmony_ci		sizeof(cap->bus_info));
21362306a36Sopenharmony_ci	return 0;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic int
21762306a36Sopenharmony_ciuvc_v4l2_get_format(struct file *file, void *fh, struct v4l2_format *fmt)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
22062306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
22162306a36Sopenharmony_ci	struct uvc_video *video = &uvc->video;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	fmt->fmt.pix.pixelformat = video->fcc;
22462306a36Sopenharmony_ci	fmt->fmt.pix.width = video->width;
22562306a36Sopenharmony_ci	fmt->fmt.pix.height = video->height;
22662306a36Sopenharmony_ci	fmt->fmt.pix.field = V4L2_FIELD_NONE;
22762306a36Sopenharmony_ci	fmt->fmt.pix.bytesperline = video->bpp * video->width / 8;
22862306a36Sopenharmony_ci	fmt->fmt.pix.sizeimage = video->imagesize;
22962306a36Sopenharmony_ci	fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
23062306a36Sopenharmony_ci	fmt->fmt.pix.priv = 0;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	return 0;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int
23662306a36Sopenharmony_ciuvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
23962306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
24062306a36Sopenharmony_ci	struct uvc_video *video = &uvc->video;
24162306a36Sopenharmony_ci	struct uvcg_format *uformat;
24262306a36Sopenharmony_ci	struct uvcg_frame *uframe;
24362306a36Sopenharmony_ci	u8 *fcc;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (fmt->type != video->queue.queue.type)
24662306a36Sopenharmony_ci		return -EINVAL;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	fcc = (u8 *)&fmt->fmt.pix.pixelformat;
24962306a36Sopenharmony_ci	uvcg_dbg(&uvc->func, "Trying format 0x%08x (%c%c%c%c): %ux%u\n",
25062306a36Sopenharmony_ci		fmt->fmt.pix.pixelformat,
25162306a36Sopenharmony_ci		fcc[0], fcc[1], fcc[2], fcc[3],
25262306a36Sopenharmony_ci		fmt->fmt.pix.width, fmt->fmt.pix.height);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	uformat = find_format_by_pix(uvc, fmt->fmt.pix.pixelformat);
25562306a36Sopenharmony_ci	if (!uformat)
25662306a36Sopenharmony_ci		return -EINVAL;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	uframe = find_closest_frame_by_size(uvc, uformat,
25962306a36Sopenharmony_ci				fmt->fmt.pix.width, fmt->fmt.pix.height);
26062306a36Sopenharmony_ci	if (!uframe)
26162306a36Sopenharmony_ci		return -EINVAL;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	fmt->fmt.pix.width = uframe->frame.w_width;
26462306a36Sopenharmony_ci	fmt->fmt.pix.height = uframe->frame.w_height;
26562306a36Sopenharmony_ci	fmt->fmt.pix.field = V4L2_FIELD_NONE;
26662306a36Sopenharmony_ci	fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(uformat, uframe);
26762306a36Sopenharmony_ci	fmt->fmt.pix.sizeimage = uvc_get_frame_size(uformat, uframe);
26862306a36Sopenharmony_ci	fmt->fmt.pix.pixelformat = to_uvc_format(uformat)->fcc;
26962306a36Sopenharmony_ci	fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
27062306a36Sopenharmony_ci	fmt->fmt.pix.priv = 0;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return 0;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic int
27662306a36Sopenharmony_ciuvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
27962306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
28062306a36Sopenharmony_ci	struct uvc_video *video = &uvc->video;
28162306a36Sopenharmony_ci	int ret;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	ret = uvc_v4l2_try_format(file, fh, fmt);
28462306a36Sopenharmony_ci	if (ret)
28562306a36Sopenharmony_ci		return ret;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	video->fcc = fmt->fmt.pix.pixelformat;
28862306a36Sopenharmony_ci	video->bpp = fmt->fmt.pix.bytesperline * 8 / video->width;
28962306a36Sopenharmony_ci	video->width = fmt->fmt.pix.width;
29062306a36Sopenharmony_ci	video->height = fmt->fmt.pix.height;
29162306a36Sopenharmony_ci	video->imagesize = fmt->fmt.pix.sizeimage;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	return ret;
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic int
29762306a36Sopenharmony_ciuvc_v4l2_enum_frameintervals(struct file *file, void *fh,
29862306a36Sopenharmony_ci		struct v4l2_frmivalenum *fival)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
30162306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
30262306a36Sopenharmony_ci	struct uvcg_format *uformat = NULL;
30362306a36Sopenharmony_ci	struct uvcg_frame *uframe = NULL;
30462306a36Sopenharmony_ci	struct uvcg_frame_ptr *frame;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	uformat = find_format_by_pix(uvc, fival->pixel_format);
30762306a36Sopenharmony_ci	if (!uformat)
30862306a36Sopenharmony_ci		return -EINVAL;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	list_for_each_entry(frame, &uformat->frames, entry) {
31162306a36Sopenharmony_ci		if (frame->frm->frame.w_width == fival->width &&
31262306a36Sopenharmony_ci		    frame->frm->frame.w_height == fival->height) {
31362306a36Sopenharmony_ci			uframe = frame->frm;
31462306a36Sopenharmony_ci			break;
31562306a36Sopenharmony_ci		}
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci	if (!uframe)
31862306a36Sopenharmony_ci		return -EINVAL;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (fival->index >= uframe->frame.b_frame_interval_type)
32162306a36Sopenharmony_ci		return -EINVAL;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	fival->discrete.numerator =
32462306a36Sopenharmony_ci		uframe->dw_frame_interval[fival->index];
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	/* TODO: handle V4L2_FRMIVAL_TYPE_STEPWISE */
32762306a36Sopenharmony_ci	fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
32862306a36Sopenharmony_ci	fival->discrete.denominator = 10000000;
32962306a36Sopenharmony_ci	v4l2_simplify_fraction(&fival->discrete.numerator,
33062306a36Sopenharmony_ci		&fival->discrete.denominator, 8, 333);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	return 0;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic int
33662306a36Sopenharmony_ciuvc_v4l2_enum_framesizes(struct file *file, void *fh,
33762306a36Sopenharmony_ci		struct v4l2_frmsizeenum *fsize)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
34062306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
34162306a36Sopenharmony_ci	struct uvcg_format *uformat = NULL;
34262306a36Sopenharmony_ci	struct uvcg_frame *uframe = NULL;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	uformat = find_format_by_pix(uvc, fsize->pixel_format);
34562306a36Sopenharmony_ci	if (!uformat)
34662306a36Sopenharmony_ci		return -EINVAL;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (fsize->index >= uformat->num_frames)
34962306a36Sopenharmony_ci		return -EINVAL;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	uframe = find_frame_by_index(uvc, uformat, fsize->index + 1);
35262306a36Sopenharmony_ci	if (!uframe)
35362306a36Sopenharmony_ci		return -EINVAL;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
35662306a36Sopenharmony_ci	fsize->discrete.width = uframe->frame.w_width;
35762306a36Sopenharmony_ci	fsize->discrete.height = uframe->frame.w_height;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	return 0;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic int
36362306a36Sopenharmony_ciuvc_v4l2_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
36662306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
36762306a36Sopenharmony_ci	const struct uvc_format_desc *fmtdesc;
36862306a36Sopenharmony_ci	struct uvcg_format *uformat;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (f->index >= uvc->header->num_fmt)
37162306a36Sopenharmony_ci		return -EINVAL;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	uformat = find_format_by_index(uvc, f->index + 1);
37462306a36Sopenharmony_ci	if (!uformat)
37562306a36Sopenharmony_ci		return -EINVAL;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	fmtdesc = to_uvc_format(uformat);
37862306a36Sopenharmony_ci	f->pixelformat = fmtdesc->fcc;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return 0;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic int
38462306a36Sopenharmony_ciuvc_v4l2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
38762306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
38862306a36Sopenharmony_ci	struct uvc_video *video = &uvc->video;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (b->type != video->queue.queue.type)
39162306a36Sopenharmony_ci		return -EINVAL;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	return uvcg_alloc_buffers(&video->queue, b);
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic int
39762306a36Sopenharmony_ciuvc_v4l2_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
40062306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
40162306a36Sopenharmony_ci	struct uvc_video *video = &uvc->video;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	return uvcg_query_buffer(&video->queue, b);
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic int
40762306a36Sopenharmony_ciuvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
41062306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
41162306a36Sopenharmony_ci	struct uvc_video *video = &uvc->video;
41262306a36Sopenharmony_ci	int ret;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	ret = uvcg_queue_buffer(&video->queue, b);
41562306a36Sopenharmony_ci	if (ret < 0)
41662306a36Sopenharmony_ci		return ret;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (uvc->state == UVC_STATE_STREAMING)
41962306a36Sopenharmony_ci		queue_work(video->async_wq, &video->pump);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	return ret;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic int
42562306a36Sopenharmony_ciuvc_v4l2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
42862306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
42962306a36Sopenharmony_ci	struct uvc_video *video = &uvc->video;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return uvcg_dequeue_buffer(&video->queue, b, file->f_flags & O_NONBLOCK);
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic int
43562306a36Sopenharmony_ciuvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
43862306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
43962306a36Sopenharmony_ci	struct uvc_video *video = &uvc->video;
44062306a36Sopenharmony_ci	int ret;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (type != video->queue.queue.type)
44362306a36Sopenharmony_ci		return -EINVAL;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/* Enable UVC video. */
44662306a36Sopenharmony_ci	ret = uvcg_video_enable(video, 1);
44762306a36Sopenharmony_ci	if (ret < 0)
44862306a36Sopenharmony_ci		return ret;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	/*
45162306a36Sopenharmony_ci	 * Complete the alternate setting selection setup phase now that
45262306a36Sopenharmony_ci	 * userspace is ready to provide video frames.
45362306a36Sopenharmony_ci	 */
45462306a36Sopenharmony_ci	uvc_function_setup_continue(uvc);
45562306a36Sopenharmony_ci	uvc->state = UVC_STATE_STREAMING;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	return 0;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic int
46162306a36Sopenharmony_ciuvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
46462306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
46562306a36Sopenharmony_ci	struct uvc_video *video = &uvc->video;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	if (type != video->queue.queue.type)
46862306a36Sopenharmony_ci		return -EINVAL;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	return uvcg_video_enable(video, 0);
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic int
47462306a36Sopenharmony_ciuvc_v4l2_subscribe_event(struct v4l2_fh *fh,
47562306a36Sopenharmony_ci			 const struct v4l2_event_subscription *sub)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(fh->vdev);
47862306a36Sopenharmony_ci	struct uvc_file_handle *handle = to_uvc_file_handle(fh);
47962306a36Sopenharmony_ci	int ret;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
48262306a36Sopenharmony_ci		return -EINVAL;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	if (sub->type == UVC_EVENT_SETUP && uvc->func_connected)
48562306a36Sopenharmony_ci		return -EBUSY;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	ret = v4l2_event_subscribe(fh, sub, 2, NULL);
48862306a36Sopenharmony_ci	if (ret < 0)
48962306a36Sopenharmony_ci		return ret;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	if (sub->type == UVC_EVENT_SETUP) {
49262306a36Sopenharmony_ci		uvc->func_connected = true;
49362306a36Sopenharmony_ci		handle->is_uvc_app_handle = true;
49462306a36Sopenharmony_ci		uvc_function_connect(uvc);
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	return 0;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic void uvc_v4l2_disable(struct uvc_device *uvc)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	uvc_function_disconnect(uvc);
50362306a36Sopenharmony_ci	uvcg_video_enable(&uvc->video, 0);
50462306a36Sopenharmony_ci	uvcg_free_buffers(&uvc->video.queue);
50562306a36Sopenharmony_ci	uvc->func_connected = false;
50662306a36Sopenharmony_ci	wake_up_interruptible(&uvc->func_connected_queue);
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic int
51062306a36Sopenharmony_ciuvc_v4l2_unsubscribe_event(struct v4l2_fh *fh,
51162306a36Sopenharmony_ci			   const struct v4l2_event_subscription *sub)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(fh->vdev);
51462306a36Sopenharmony_ci	struct uvc_file_handle *handle = to_uvc_file_handle(fh);
51562306a36Sopenharmony_ci	int ret;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	ret = v4l2_event_unsubscribe(fh, sub);
51862306a36Sopenharmony_ci	if (ret < 0)
51962306a36Sopenharmony_ci		return ret;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	if (sub->type == UVC_EVENT_SETUP && handle->is_uvc_app_handle) {
52262306a36Sopenharmony_ci		uvc_v4l2_disable(uvc);
52362306a36Sopenharmony_ci		handle->is_uvc_app_handle = false;
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	return 0;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic long
53062306a36Sopenharmony_ciuvc_v4l2_ioctl_default(struct file *file, void *fh, bool valid_prio,
53162306a36Sopenharmony_ci		       unsigned int cmd, void *arg)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
53462306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	switch (cmd) {
53762306a36Sopenharmony_ci	case UVCIOC_SEND_RESPONSE:
53862306a36Sopenharmony_ci		return uvc_send_response(uvc, arg);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	default:
54162306a36Sopenharmony_ci		return -ENOIOCTLCMD;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ciconst struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = {
54662306a36Sopenharmony_ci	.vidioc_querycap = uvc_v4l2_querycap,
54762306a36Sopenharmony_ci	.vidioc_try_fmt_vid_out = uvc_v4l2_try_format,
54862306a36Sopenharmony_ci	.vidioc_g_fmt_vid_out = uvc_v4l2_get_format,
54962306a36Sopenharmony_ci	.vidioc_s_fmt_vid_out = uvc_v4l2_set_format,
55062306a36Sopenharmony_ci	.vidioc_enum_frameintervals = uvc_v4l2_enum_frameintervals,
55162306a36Sopenharmony_ci	.vidioc_enum_framesizes = uvc_v4l2_enum_framesizes,
55262306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_out = uvc_v4l2_enum_format,
55362306a36Sopenharmony_ci	.vidioc_reqbufs = uvc_v4l2_reqbufs,
55462306a36Sopenharmony_ci	.vidioc_querybuf = uvc_v4l2_querybuf,
55562306a36Sopenharmony_ci	.vidioc_qbuf = uvc_v4l2_qbuf,
55662306a36Sopenharmony_ci	.vidioc_dqbuf = uvc_v4l2_dqbuf,
55762306a36Sopenharmony_ci	.vidioc_streamon = uvc_v4l2_streamon,
55862306a36Sopenharmony_ci	.vidioc_streamoff = uvc_v4l2_streamoff,
55962306a36Sopenharmony_ci	.vidioc_subscribe_event = uvc_v4l2_subscribe_event,
56062306a36Sopenharmony_ci	.vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event,
56162306a36Sopenharmony_ci	.vidioc_default = uvc_v4l2_ioctl_default,
56262306a36Sopenharmony_ci};
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci/* --------------------------------------------------------------------------
56562306a36Sopenharmony_ci * V4L2
56662306a36Sopenharmony_ci */
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic int
56962306a36Sopenharmony_ciuvc_v4l2_open(struct file *file)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
57262306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
57362306a36Sopenharmony_ci	struct uvc_file_handle *handle;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	handle = kzalloc(sizeof(*handle), GFP_KERNEL);
57662306a36Sopenharmony_ci	if (handle == NULL)
57762306a36Sopenharmony_ci		return -ENOMEM;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	v4l2_fh_init(&handle->vfh, vdev);
58062306a36Sopenharmony_ci	v4l2_fh_add(&handle->vfh);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	handle->device = &uvc->video;
58362306a36Sopenharmony_ci	file->private_data = &handle->vfh;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	return 0;
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic int
58962306a36Sopenharmony_ciuvc_v4l2_release(struct file *file)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
59262306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
59362306a36Sopenharmony_ci	struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
59462306a36Sopenharmony_ci	struct uvc_video *video = handle->device;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	mutex_lock(&video->mutex);
59762306a36Sopenharmony_ci	if (handle->is_uvc_app_handle)
59862306a36Sopenharmony_ci		uvc_v4l2_disable(uvc);
59962306a36Sopenharmony_ci	mutex_unlock(&video->mutex);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	file->private_data = NULL;
60262306a36Sopenharmony_ci	v4l2_fh_del(&handle->vfh);
60362306a36Sopenharmony_ci	v4l2_fh_exit(&handle->vfh);
60462306a36Sopenharmony_ci	kfree(handle);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	return 0;
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic int
61062306a36Sopenharmony_ciuvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
61362306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	return uvcg_queue_mmap(&uvc->video.queue, vma);
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic __poll_t
61962306a36Sopenharmony_ciuvc_v4l2_poll(struct file *file, poll_table *wait)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
62262306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	return uvcg_queue_poll(&uvc->video.queue, file, wait);
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci#ifndef CONFIG_MMU
62862306a36Sopenharmony_cistatic unsigned long uvcg_v4l2_get_unmapped_area(struct file *file,
62962306a36Sopenharmony_ci		unsigned long addr, unsigned long len, unsigned long pgoff,
63062306a36Sopenharmony_ci		unsigned long flags)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	struct video_device *vdev = video_devdata(file);
63362306a36Sopenharmony_ci	struct uvc_device *uvc = video_get_drvdata(vdev);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	return uvcg_queue_get_unmapped_area(&uvc->video.queue, pgoff);
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci#endif
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ciconst struct v4l2_file_operations uvc_v4l2_fops = {
64062306a36Sopenharmony_ci	.owner		= THIS_MODULE,
64162306a36Sopenharmony_ci	.open		= uvc_v4l2_open,
64262306a36Sopenharmony_ci	.release	= uvc_v4l2_release,
64362306a36Sopenharmony_ci	.unlocked_ioctl	= video_ioctl2,
64462306a36Sopenharmony_ci	.mmap		= uvc_v4l2_mmap,
64562306a36Sopenharmony_ci	.poll		= uvc_v4l2_poll,
64662306a36Sopenharmony_ci#ifndef CONFIG_MMU
64762306a36Sopenharmony_ci	.get_unmapped_area = uvcg_v4l2_get_unmapped_area,
64862306a36Sopenharmony_ci#endif
64962306a36Sopenharmony_ci};
65062306a36Sopenharmony_ci
651