File Index
Files and directories
Sign in
FILE

uvc_metadata.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/usb/uvc/uvc_metadata.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 *      uvc_metadata.c  --  USB Video Class driver - Metadata handling
  4 *
  5 *      Copyright (C) 2016
  6 *          Guennadi Liakhovetski (guennadi.liakhovetski@intel.com)
  7 */
  8
  9#include <linux/kernel.h>
 10#include <linux/list.h>
 11#include <linux/module.h>
 12#include <linux/usb.h>
 13#include <linux/usb/uvc.h>
 14#include <linux/videodev2.h>
 15
 16#include <media/v4l2-ioctl.h>
 17#include <media/videobuf2-v4l2.h>
 18#include <media/videobuf2-vmalloc.h>
 19
 20#include "uvcvideo.h"
 21
 22/* -----------------------------------------------------------------------------
 23 * V4L2 ioctls
 24 */
 25
 26static int uvc_meta_v4l2_querycap(struct file *file, void *priv,
 27				  struct v4l2_capability *cap)
 28{
 29	struct v4l2_fh *vfh = file_to_v4l2_fh(file);
 30	struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
 31	struct uvc_video_chain *chain = stream->chain;
 32
 33	strscpy(cap->driver, "uvcvideo", sizeof(cap->driver));
 34	strscpy(cap->card, stream->dev->name, sizeof(cap->card));
 35	usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info));
 36	cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
 37			  | chain->caps;
 38
 39	return 0;
 40}
 41
 42static int uvc_meta_v4l2_get_format(struct file *file, void *priv,
 43				    struct v4l2_format *format)
 44{
 45	struct v4l2_fh *vfh = file_to_v4l2_fh(file);
 46	struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
 47	struct v4l2_meta_format *fmt = &format->fmt.meta;
 48
 49	if (format->type != vfh->vdev->queue->type)
 50		return -EINVAL;
 51
 52	fmt->dataformat = stream->meta.format;
 53	fmt->buffersize = UVC_METADATA_BUF_SIZE;
 54
 55	return 0;
 56}
 57
 58static int uvc_meta_v4l2_try_format(struct file *file, void *priv,
 59				    struct v4l2_format *format)
 60{
 61	struct v4l2_fh *vfh = file_to_v4l2_fh(file);
 62	struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
 63	struct uvc_device *dev = stream->dev;
 64	struct v4l2_meta_format *fmt = &format->fmt.meta;
 65	u32 fmeta = V4L2_META_FMT_UVC;
 66
 67	if (format->type != vfh->vdev->queue->type)
 68		return -EINVAL;
 69
 70	for (unsigned int i = 0; i < dev->nmeta_formats; i++) {
 71		if (dev->meta_formats[i] == fmt->dataformat) {
 72			fmeta = fmt->dataformat;
 73			break;
 74		}
 75	}
 76
 77	memset(fmt, 0, sizeof(*fmt));
 78
 79	fmt->dataformat = fmeta;
 80	fmt->buffersize = UVC_METADATA_BUF_SIZE;
 81
 82	return 0;
 83}
 84
 85static int uvc_meta_v4l2_set_format(struct file *file, void *priv,
 86				    struct v4l2_format *format)
 87{
 88	struct v4l2_fh *vfh = file_to_v4l2_fh(file);
 89	struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
 90	struct v4l2_meta_format *fmt = &format->fmt.meta;
 91	int ret;
 92
 93	ret = uvc_meta_v4l2_try_format(file, priv, format);
 94	if (ret < 0)
 95		return ret;
 96
 97	/*
 98	 * We could in principle switch at any time, also during streaming.
 99	 * Metadata buffers would still be perfectly parseable, but it's more
100	 * consistent and cleaner to disallow that.
101	 */
102	if (vb2_is_busy(&stream->meta.queue.queue))
103		return -EBUSY;
104
105	stream->meta.format = fmt->dataformat;
106
107	return 0;
108}
109
110static int uvc_meta_v4l2_enum_formats(struct file *file, void *priv,
111				      struct v4l2_fmtdesc *fdesc)
112{
113	struct v4l2_fh *vfh = file_to_v4l2_fh(file);
114	struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
115	struct uvc_device *dev = stream->dev;
116
117	if (fdesc->type != vfh->vdev->queue->type)
118		return -EINVAL;
119
120	if (fdesc->index >= dev->nmeta_formats)
121		return -EINVAL;
122
123	fdesc->pixelformat = dev->meta_formats[fdesc->index];
124
125	return 0;
126}
127
128static const struct v4l2_ioctl_ops uvc_meta_ioctl_ops = {
129	.vidioc_querycap		= uvc_meta_v4l2_querycap,
130	.vidioc_g_fmt_meta_cap		= uvc_meta_v4l2_get_format,
131	.vidioc_s_fmt_meta_cap		= uvc_meta_v4l2_set_format,
132	.vidioc_try_fmt_meta_cap	= uvc_meta_v4l2_try_format,
133	.vidioc_enum_fmt_meta_cap	= uvc_meta_v4l2_enum_formats,
134	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
135	.vidioc_querybuf		= vb2_ioctl_querybuf,
136	.vidioc_qbuf			= vb2_ioctl_qbuf,
137	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
138	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
139	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
140	.vidioc_streamon		= vb2_ioctl_streamon,
141	.vidioc_streamoff		= vb2_ioctl_streamoff,
142};
143
144/* -----------------------------------------------------------------------------
145 * V4L2 File Operations
146 */
147
148static const struct v4l2_file_operations uvc_meta_fops = {
149	.owner = THIS_MODULE,
150	.unlocked_ioctl = video_ioctl2,
151	.open = v4l2_fh_open,
152	.release = vb2_fop_release,
153	.poll = vb2_fop_poll,
154	.mmap = vb2_fop_mmap,
155};
156
157static struct uvc_entity *uvc_meta_find_msxu(struct uvc_device *dev)
158{
159	static const u8 uvc_msxu_guid[16] = UVC_GUID_MSXU_1_5;
160	struct uvc_entity *entity;
161
162	list_for_each_entry(entity, &dev->entities, list) {
163		if (!memcmp(entity->guid, uvc_msxu_guid, sizeof(entity->guid)))
164			return entity;
165	}
166
167	return NULL;
168}
169
170static int uvc_meta_detect_msxu(struct uvc_device *dev)
171{
172	u32 *data __free(kfree) = NULL;
173	struct uvc_entity *entity;
174	int ret;
175
176	entity = uvc_meta_find_msxu(dev);
177	if (!entity)
178		return 0;
179
180	/*
181	 * USB requires buffers aligned in a special way, simplest way is to
182	 * make sure that query_ctrl will work is to kmalloc() them.
183	 */
184	data = kmalloc(sizeof(*data), GFP_KERNEL);
185	if (!data)
186		return -ENOMEM;
187
188	/*
189	 * Check if the metadata is already enabled, or if the device always
190	 * returns metadata.
191	 */
192	ret = uvc_query_ctrl(dev, UVC_GET_CUR, entity->id, dev->intfnum,
193			     UVC_MSXU_CONTROL_METADATA, data, sizeof(*data));
194	if (ret)
195		return 0;
196
197	if (*data) {
198		dev->quirks |= UVC_QUIRK_MSXU_META;
199		return 0;
200	}
201
202	/*
203	 * Set the value of UVC_MSXU_CONTROL_METADATA to the value reported by
204	 * GET_MAX to enable production of MSXU metadata. The GET_MAX request
205	 * reports the maximum size of the metadata, if its value is 0 then MSXU
206	 * metadata is not supported. For more information, see
207	 * https://learn.microsoft.com/en-us/windows-hardware/drivers/stream/uvc-extensions-1-5#2229-metadata-control
208	 */
209	ret = uvc_query_ctrl(dev, UVC_GET_MAX, entity->id, dev->intfnum,
210			     UVC_MSXU_CONTROL_METADATA, data, sizeof(*data));
211	if (ret || !*data)
212		return 0;
213
214	/*
215	 * If we can set UVC_MSXU_CONTROL_METADATA, the device will report
216	 * metadata.
217	 */
218	ret = uvc_query_ctrl(dev, UVC_SET_CUR, entity->id, dev->intfnum,
219			     UVC_MSXU_CONTROL_METADATA, data, sizeof(*data));
220	if (!ret)
221		dev->quirks |= UVC_QUIRK_MSXU_META;
222
223	return 0;
224}
225
226int uvc_meta_register(struct uvc_streaming *stream)
227{
228	struct uvc_device *dev = stream->dev;
229	struct uvc_video_queue *queue = &stream->meta.queue;
230
231	stream->meta.format = V4L2_META_FMT_UVC;
232
233	return uvc_register_video_device(dev, stream, queue,
234					 V4L2_BUF_TYPE_META_CAPTURE,
235					 &uvc_meta_fops, &uvc_meta_ioctl_ops);
236}
237
238int uvc_meta_init(struct uvc_device *dev)
239{
240	unsigned int i = 0;
241	int ret;
242
243	ret = uvc_meta_detect_msxu(dev);
244	if (ret)
245		return ret;
246
247	dev->meta_formats[i++] = V4L2_META_FMT_UVC;
248
249	if (dev->info->meta_format &&
250	    !WARN_ON(dev->info->meta_format == V4L2_META_FMT_UVC))
251		dev->meta_formats[i++] = dev->info->meta_format;
252
253	if (dev->quirks & UVC_QUIRK_MSXU_META &&
254	    !WARN_ON(dev->info->meta_format == V4L2_META_FMT_UVC_MSXU_1_5))
255		dev->meta_formats[i++] = V4L2_META_FMT_UVC_MSXU_1_5;
256
257	 /* IMPORTANT: for new meta-formats update UVC_MAX_META_DATA_FORMATS. */
258	dev->nmeta_formats = i;
259
260	return 0;
261}
~/ Read-only file preview.