From 088ead25524583e2200aa99111bea2f66a86545a Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 6 Dec 2017 10:15:40 -0500 Subject: media: uvcvideo: Add a metadata device node Some UVC video cameras contain metadata in their payload headers. This patch extracts that data, adding more clock synchronisation information, on both bulk and isochronous endpoints and makes it available to the user space on a separate video node, using the V4L2_CAP_META_CAPTURE capability and the V4L2_BUF_TYPE_META_CAPTURE buffer queue type. By default, only the V4L2_META_FMT_UVC pixel format is available from those nodes. However, cameras can be added to the device ID table to additionally specify their own metadata format, in which case that format will also become available from the metadata node. [Use put_unaligned instead of __put_unaligned_cpu64] [Use put_unaligned for the sof field as well] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/uvc/uvc_metadata.c | 179 +++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 drivers/media/usb/uvc/uvc_metadata.c (limited to 'drivers/media/usb/uvc/uvc_metadata.c') diff --git a/drivers/media/usb/uvc/uvc_metadata.c b/drivers/media/usb/uvc/uvc_metadata.c new file mode 100644 index 000000000000..cd1aec19cc5b --- /dev/null +++ b/drivers/media/usb/uvc/uvc_metadata.c @@ -0,0 +1,179 @@ +/* + * uvc_metadata.c -- USB Video Class driver - Metadata handling + * + * Copyright (C) 2016 + * Guennadi Liakhovetski (guennadi.liakhovetski@intel.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "uvcvideo.h" + +/* ----------------------------------------------------------------------------- + * V4L2 ioctls + */ + +static int uvc_meta_v4l2_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct v4l2_fh *vfh = file->private_data; + struct uvc_streaming *stream = video_get_drvdata(vfh->vdev); + struct uvc_video_chain *chain = stream->chain; + + strlcpy(cap->driver, "uvcvideo", sizeof(cap->driver)); + strlcpy(cap->card, vfh->vdev->name, sizeof(cap->card)); + usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING + | chain->caps; + + return 0; +} + +static int uvc_meta_v4l2_get_format(struct file *file, void *fh, + struct v4l2_format *format) +{ + struct v4l2_fh *vfh = file->private_data; + struct uvc_streaming *stream = video_get_drvdata(vfh->vdev); + struct v4l2_meta_format *fmt = &format->fmt.meta; + + if (format->type != vfh->vdev->queue->type) + return -EINVAL; + + memset(fmt, 0, sizeof(*fmt)); + + fmt->dataformat = stream->meta.format; + fmt->buffersize = UVC_METATADA_BUF_SIZE; + + return 0; +} + +static int uvc_meta_v4l2_try_format(struct file *file, void *fh, + struct v4l2_format *format) +{ + struct v4l2_fh *vfh = file->private_data; + struct uvc_streaming *stream = video_get_drvdata(vfh->vdev); + struct uvc_device *dev = stream->dev; + struct v4l2_meta_format *fmt = &format->fmt.meta; + u32 fmeta = fmt->dataformat; + + if (format->type != vfh->vdev->queue->type) + return -EINVAL; + + memset(fmt, 0, sizeof(*fmt)); + + fmt->dataformat = fmeta == dev->meta_format ? fmeta : V4L2_META_FMT_UVC; + fmt->buffersize = UVC_METATADA_BUF_SIZE; + + return 0; +} + +static int uvc_meta_v4l2_set_format(struct file *file, void *fh, + struct v4l2_format *format) +{ + struct v4l2_fh *vfh = file->private_data; + struct uvc_streaming *stream = video_get_drvdata(vfh->vdev); + struct v4l2_meta_format *fmt = &format->fmt.meta; + int ret; + + ret = uvc_meta_v4l2_try_format(file, fh, format); + if (ret < 0) + return ret; + + /* + * We could in principle switch at any time, also during streaming. + * Metadata buffers would still be perfectly parseable, but it's more + * consistent and cleaner to disallow that. + */ + mutex_lock(&stream->mutex); + + if (uvc_queue_allocated(&stream->queue)) + ret = -EBUSY; + else + stream->meta.format = fmt->dataformat; + + mutex_unlock(&stream->mutex); + + return ret; +} + +static int uvc_meta_v4l2_enum_formats(struct file *file, void *fh, + struct v4l2_fmtdesc *fdesc) +{ + struct v4l2_fh *vfh = file->private_data; + struct uvc_streaming *stream = video_get_drvdata(vfh->vdev); + struct uvc_device *dev = stream->dev; + u32 index = fdesc->index; + + if (fdesc->type != vfh->vdev->queue->type || + index > 1U || (index && !dev->meta_format)) + return -EINVAL; + + memset(fdesc, 0, sizeof(*fdesc)); + + fdesc->type = vfh->vdev->queue->type; + fdesc->index = index; + fdesc->pixelformat = index ? dev->meta_format : V4L2_META_FMT_UVC; + + return 0; +} + +static const struct v4l2_ioctl_ops uvc_meta_ioctl_ops = { + .vidioc_querycap = uvc_meta_v4l2_querycap, + .vidioc_g_fmt_meta_cap = uvc_meta_v4l2_get_format, + .vidioc_s_fmt_meta_cap = uvc_meta_v4l2_set_format, + .vidioc_try_fmt_meta_cap = uvc_meta_v4l2_try_format, + .vidioc_enum_fmt_meta_cap = uvc_meta_v4l2_enum_formats, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 File Operations + */ + +static const struct v4l2_file_operations uvc_meta_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +}; + +int uvc_meta_register(struct uvc_streaming *stream) +{ + struct uvc_device *dev = stream->dev; + struct video_device *vdev = &stream->meta.vdev; + struct uvc_video_queue *queue = &stream->meta.queue; + + stream->meta.format = V4L2_META_FMT_UVC; + + /* + * The video interface queue uses manual locking and thus does not set + * the queue pointer. Set it manually here. + */ + vdev->queue = &queue->queue; + + return uvc_register_video_device(dev, stream, vdev, queue, + V4L2_BUF_TYPE_META_CAPTURE, + &uvc_meta_fops, &uvc_meta_ioctl_ops); +} -- cgit