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