xref: /openbmc/linux/drivers/media/usb/uvc/uvc_metadata.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2088ead25SGuennadi Liakhovetski /*
3088ead25SGuennadi Liakhovetski  *      uvc_metadata.c  --  USB Video Class driver - Metadata handling
4088ead25SGuennadi Liakhovetski  *
5088ead25SGuennadi Liakhovetski  *      Copyright (C) 2016
6088ead25SGuennadi Liakhovetski  *          Guennadi Liakhovetski (guennadi.liakhovetski@intel.com)
7088ead25SGuennadi Liakhovetski  */
8088ead25SGuennadi Liakhovetski 
9088ead25SGuennadi Liakhovetski #include <linux/kernel.h>
10088ead25SGuennadi Liakhovetski #include <linux/list.h>
11088ead25SGuennadi Liakhovetski #include <linux/module.h>
12088ead25SGuennadi Liakhovetski #include <linux/usb.h>
13088ead25SGuennadi Liakhovetski #include <linux/videodev2.h>
14088ead25SGuennadi Liakhovetski 
15088ead25SGuennadi Liakhovetski #include <media/v4l2-ioctl.h>
16088ead25SGuennadi Liakhovetski #include <media/videobuf2-v4l2.h>
17088ead25SGuennadi Liakhovetski #include <media/videobuf2-vmalloc.h>
18088ead25SGuennadi Liakhovetski 
19088ead25SGuennadi Liakhovetski #include "uvcvideo.h"
20088ead25SGuennadi Liakhovetski 
21088ead25SGuennadi Liakhovetski /* -----------------------------------------------------------------------------
22088ead25SGuennadi Liakhovetski  * V4L2 ioctls
23088ead25SGuennadi Liakhovetski  */
24088ead25SGuennadi Liakhovetski 
uvc_meta_v4l2_querycap(struct file * file,void * fh,struct v4l2_capability * cap)25088ead25SGuennadi Liakhovetski static int uvc_meta_v4l2_querycap(struct file *file, void *fh,
26088ead25SGuennadi Liakhovetski 				  struct v4l2_capability *cap)
27088ead25SGuennadi Liakhovetski {
28088ead25SGuennadi Liakhovetski 	struct v4l2_fh *vfh = file->private_data;
29088ead25SGuennadi Liakhovetski 	struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
30088ead25SGuennadi Liakhovetski 	struct uvc_video_chain *chain = stream->chain;
31088ead25SGuennadi Liakhovetski 
32c0decac1SMauro Carvalho Chehab 	strscpy(cap->driver, "uvcvideo", sizeof(cap->driver));
33*457e7911SRicardo Ribalda 	strscpy(cap->card, stream->dev->name, sizeof(cap->card));
34088ead25SGuennadi Liakhovetski 	usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info));
35088ead25SGuennadi Liakhovetski 	cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
36088ead25SGuennadi Liakhovetski 			  | chain->caps;
37088ead25SGuennadi Liakhovetski 
38088ead25SGuennadi Liakhovetski 	return 0;
39088ead25SGuennadi Liakhovetski }
40088ead25SGuennadi Liakhovetski 
uvc_meta_v4l2_get_format(struct file * file,void * fh,struct v4l2_format * format)41088ead25SGuennadi Liakhovetski static int uvc_meta_v4l2_get_format(struct file *file, void *fh,
42088ead25SGuennadi Liakhovetski 				    struct v4l2_format *format)
43088ead25SGuennadi Liakhovetski {
44088ead25SGuennadi Liakhovetski 	struct v4l2_fh *vfh = file->private_data;
45088ead25SGuennadi Liakhovetski 	struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
46088ead25SGuennadi Liakhovetski 	struct v4l2_meta_format *fmt = &format->fmt.meta;
47088ead25SGuennadi Liakhovetski 
48088ead25SGuennadi Liakhovetski 	if (format->type != vfh->vdev->queue->type)
49088ead25SGuennadi Liakhovetski 		return -EINVAL;
50088ead25SGuennadi Liakhovetski 
51088ead25SGuennadi Liakhovetski 	memset(fmt, 0, sizeof(*fmt));
52088ead25SGuennadi Liakhovetski 
53088ead25SGuennadi Liakhovetski 	fmt->dataformat = stream->meta.format;
54ac7dabf1SChristophe JAILLET 	fmt->buffersize = UVC_METADATA_BUF_SIZE;
55088ead25SGuennadi Liakhovetski 
56088ead25SGuennadi Liakhovetski 	return 0;
57088ead25SGuennadi Liakhovetski }
58088ead25SGuennadi Liakhovetski 
uvc_meta_v4l2_try_format(struct file * file,void * fh,struct v4l2_format * format)59088ead25SGuennadi Liakhovetski static int uvc_meta_v4l2_try_format(struct file *file, void *fh,
60088ead25SGuennadi Liakhovetski 				    struct v4l2_format *format)
61088ead25SGuennadi Liakhovetski {
62088ead25SGuennadi Liakhovetski 	struct v4l2_fh *vfh = file->private_data;
63088ead25SGuennadi Liakhovetski 	struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
64088ead25SGuennadi Liakhovetski 	struct uvc_device *dev = stream->dev;
65088ead25SGuennadi Liakhovetski 	struct v4l2_meta_format *fmt = &format->fmt.meta;
66088ead25SGuennadi Liakhovetski 	u32 fmeta = fmt->dataformat;
67088ead25SGuennadi Liakhovetski 
68088ead25SGuennadi Liakhovetski 	if (format->type != vfh->vdev->queue->type)
69088ead25SGuennadi Liakhovetski 		return -EINVAL;
70088ead25SGuennadi Liakhovetski 
71088ead25SGuennadi Liakhovetski 	memset(fmt, 0, sizeof(*fmt));
72088ead25SGuennadi Liakhovetski 
733a03284dSLaurent Pinchart 	fmt->dataformat = fmeta == dev->info->meta_format
743a03284dSLaurent Pinchart 			? fmeta : V4L2_META_FMT_UVC;
75ac7dabf1SChristophe JAILLET 	fmt->buffersize = UVC_METADATA_BUF_SIZE;
76088ead25SGuennadi Liakhovetski 
77088ead25SGuennadi Liakhovetski 	return 0;
78088ead25SGuennadi Liakhovetski }
79088ead25SGuennadi Liakhovetski 
uvc_meta_v4l2_set_format(struct file * file,void * fh,struct v4l2_format * format)80088ead25SGuennadi Liakhovetski static int uvc_meta_v4l2_set_format(struct file *file, void *fh,
81088ead25SGuennadi Liakhovetski 				    struct v4l2_format *format)
82088ead25SGuennadi Liakhovetski {
83088ead25SGuennadi Liakhovetski 	struct v4l2_fh *vfh = file->private_data;
84088ead25SGuennadi Liakhovetski 	struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
85088ead25SGuennadi Liakhovetski 	struct v4l2_meta_format *fmt = &format->fmt.meta;
86088ead25SGuennadi Liakhovetski 	int ret;
87088ead25SGuennadi Liakhovetski 
88088ead25SGuennadi Liakhovetski 	ret = uvc_meta_v4l2_try_format(file, fh, format);
89088ead25SGuennadi Liakhovetski 	if (ret < 0)
90088ead25SGuennadi Liakhovetski 		return ret;
91088ead25SGuennadi Liakhovetski 
92088ead25SGuennadi Liakhovetski 	/*
93088ead25SGuennadi Liakhovetski 	 * We could in principle switch at any time, also during streaming.
94088ead25SGuennadi Liakhovetski 	 * Metadata buffers would still be perfectly parseable, but it's more
95088ead25SGuennadi Liakhovetski 	 * consistent and cleaner to disallow that.
96088ead25SGuennadi Liakhovetski 	 */
97088ead25SGuennadi Liakhovetski 	mutex_lock(&stream->mutex);
98088ead25SGuennadi Liakhovetski 
99088ead25SGuennadi Liakhovetski 	if (uvc_queue_allocated(&stream->queue))
100088ead25SGuennadi Liakhovetski 		ret = -EBUSY;
101088ead25SGuennadi Liakhovetski 	else
102088ead25SGuennadi Liakhovetski 		stream->meta.format = fmt->dataformat;
103088ead25SGuennadi Liakhovetski 
104088ead25SGuennadi Liakhovetski 	mutex_unlock(&stream->mutex);
105088ead25SGuennadi Liakhovetski 
106088ead25SGuennadi Liakhovetski 	return ret;
107088ead25SGuennadi Liakhovetski }
108088ead25SGuennadi Liakhovetski 
uvc_meta_v4l2_enum_formats(struct file * file,void * fh,struct v4l2_fmtdesc * fdesc)109088ead25SGuennadi Liakhovetski static int uvc_meta_v4l2_enum_formats(struct file *file, void *fh,
110088ead25SGuennadi Liakhovetski 				      struct v4l2_fmtdesc *fdesc)
111088ead25SGuennadi Liakhovetski {
112088ead25SGuennadi Liakhovetski 	struct v4l2_fh *vfh = file->private_data;
113088ead25SGuennadi Liakhovetski 	struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
114088ead25SGuennadi Liakhovetski 	struct uvc_device *dev = stream->dev;
115088ead25SGuennadi Liakhovetski 	u32 index = fdesc->index;
116088ead25SGuennadi Liakhovetski 
117088ead25SGuennadi Liakhovetski 	if (fdesc->type != vfh->vdev->queue->type ||
1183a03284dSLaurent Pinchart 	    index > 1U || (index && !dev->info->meta_format))
119088ead25SGuennadi Liakhovetski 		return -EINVAL;
120088ead25SGuennadi Liakhovetski 
121088ead25SGuennadi Liakhovetski 	memset(fdesc, 0, sizeof(*fdesc));
122088ead25SGuennadi Liakhovetski 
123088ead25SGuennadi Liakhovetski 	fdesc->type = vfh->vdev->queue->type;
124088ead25SGuennadi Liakhovetski 	fdesc->index = index;
1253a03284dSLaurent Pinchart 	fdesc->pixelformat = index ? dev->info->meta_format : V4L2_META_FMT_UVC;
126088ead25SGuennadi Liakhovetski 
127088ead25SGuennadi Liakhovetski 	return 0;
128088ead25SGuennadi Liakhovetski }
129088ead25SGuennadi Liakhovetski 
130088ead25SGuennadi Liakhovetski static const struct v4l2_ioctl_ops uvc_meta_ioctl_ops = {
131088ead25SGuennadi Liakhovetski 	.vidioc_querycap		= uvc_meta_v4l2_querycap,
132088ead25SGuennadi Liakhovetski 	.vidioc_g_fmt_meta_cap		= uvc_meta_v4l2_get_format,
133088ead25SGuennadi Liakhovetski 	.vidioc_s_fmt_meta_cap		= uvc_meta_v4l2_set_format,
134088ead25SGuennadi Liakhovetski 	.vidioc_try_fmt_meta_cap	= uvc_meta_v4l2_try_format,
135088ead25SGuennadi Liakhovetski 	.vidioc_enum_fmt_meta_cap	= uvc_meta_v4l2_enum_formats,
136088ead25SGuennadi Liakhovetski 	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
137088ead25SGuennadi Liakhovetski 	.vidioc_querybuf		= vb2_ioctl_querybuf,
138088ead25SGuennadi Liakhovetski 	.vidioc_qbuf			= vb2_ioctl_qbuf,
139088ead25SGuennadi Liakhovetski 	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
140088ead25SGuennadi Liakhovetski 	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
141088ead25SGuennadi Liakhovetski 	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
142088ead25SGuennadi Liakhovetski 	.vidioc_streamon		= vb2_ioctl_streamon,
143088ead25SGuennadi Liakhovetski 	.vidioc_streamoff		= vb2_ioctl_streamoff,
144088ead25SGuennadi Liakhovetski };
145088ead25SGuennadi Liakhovetski 
146088ead25SGuennadi Liakhovetski /* -----------------------------------------------------------------------------
147088ead25SGuennadi Liakhovetski  * V4L2 File Operations
148088ead25SGuennadi Liakhovetski  */
149088ead25SGuennadi Liakhovetski 
150088ead25SGuennadi Liakhovetski static const struct v4l2_file_operations uvc_meta_fops = {
151088ead25SGuennadi Liakhovetski 	.owner = THIS_MODULE,
152088ead25SGuennadi Liakhovetski 	.unlocked_ioctl = video_ioctl2,
153088ead25SGuennadi Liakhovetski 	.open = v4l2_fh_open,
154088ead25SGuennadi Liakhovetski 	.release = vb2_fop_release,
155088ead25SGuennadi Liakhovetski 	.poll = vb2_fop_poll,
156088ead25SGuennadi Liakhovetski 	.mmap = vb2_fop_mmap,
157088ead25SGuennadi Liakhovetski };
158088ead25SGuennadi Liakhovetski 
uvc_meta_register(struct uvc_streaming * stream)159088ead25SGuennadi Liakhovetski int uvc_meta_register(struct uvc_streaming *stream)
160088ead25SGuennadi Liakhovetski {
161088ead25SGuennadi Liakhovetski 	struct uvc_device *dev = stream->dev;
162088ead25SGuennadi Liakhovetski 	struct video_device *vdev = &stream->meta.vdev;
163088ead25SGuennadi Liakhovetski 	struct uvc_video_queue *queue = &stream->meta.queue;
164088ead25SGuennadi Liakhovetski 
165088ead25SGuennadi Liakhovetski 	stream->meta.format = V4L2_META_FMT_UVC;
166088ead25SGuennadi Liakhovetski 
167088ead25SGuennadi Liakhovetski 	/*
168088ead25SGuennadi Liakhovetski 	 * The video interface queue uses manual locking and thus does not set
169088ead25SGuennadi Liakhovetski 	 * the queue pointer. Set it manually here.
170088ead25SGuennadi Liakhovetski 	 */
171088ead25SGuennadi Liakhovetski 	vdev->queue = &queue->queue;
172088ead25SGuennadi Liakhovetski 
173088ead25SGuennadi Liakhovetski 	return uvc_register_video_device(dev, stream, vdev, queue,
174088ead25SGuennadi Liakhovetski 					 V4L2_BUF_TYPE_META_CAPTURE,
175088ead25SGuennadi Liakhovetski 					 &uvc_meta_fops, &uvc_meta_ioctl_ops);
176088ead25SGuennadi Liakhovetski }
177