Re: [PATCH v4] uvcvideo: add a metadata device node

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Laurent,

Thanks for your comments!

On Fri, 21 Jul 2017, Laurent Pinchart wrote:

> Hi Guennadi,
> 
> Thank you for the patch.
> 
> > 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. Even
> > though different cameras will have different metadata formats, we use
> > the same V4L2_META_FMT_UVC pixel format for all of them. Users have to
> > parse data, based on the specific camera model information.
> 
> The issue we still haven't addressed is how to ensure that vendors will
> document their metadata format :-S

Uhm, add a black list of offending vendors and drop 60% of their frames? 
;-)

> > This version of the patch only creates such metadata nodes for cameras,
> > specifying a UVC_QUIRK_METADATA_NODE quirk flag.
> > 
> > Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@xxxxxxxxx>
> > ---
> > 
> > v4:
> > - add support for isochronous cameras. Metadata is now collected from as 
> > many payloads as they fit in the buffer
> > - add a USB Start Of Frame and a system timestamp to each metadata block 
> > for user-space clock synchronisation
> > - use a default buffer size of 1024 bytes
> > 
> > Thanks to Laurent for patient long discussions and to everybody, who 
> > helped me conduct all the investigation into various past, present and 
> > future UVC cameras :-)
> > 
> >  drivers/media/usb/uvc/Makefile       |   2 +-
> >  drivers/media/usb/uvc/uvc_driver.c   |   4 +
> >  drivers/media/usb/uvc/uvc_isight.c   |   2 +-
> >  drivers/media/usb/uvc/uvc_metadata.c | 158 +++++++++++++++++++++++++++++++
> >  drivers/media/usb/uvc/uvc_queue.c    |  68 ++++++++++-----
> >  drivers/media/usb/uvc/uvc_video.c    | 101 +++++++++++++++++++---
> >  drivers/media/usb/uvc/uvcvideo.h     |  23 ++++-
> >  drivers/media/v4l2-core/v4l2-ioctl.c |   1 +
> >  include/uapi/linux/uvcvideo.h        |  19 +++++
> >  include/uapi/linux/videodev2.h       |   3 +
> >  10 files changed, 347 insertions(+), 34 deletions(-)
> >  create mode 100644 drivers/media/usb/uvc/uvc_metadata.c
> 
> [snip]
> 
> > diff --git a/drivers/media/usb/uvc/uvc_driver.c
> > b/drivers/media/usb/uvc/uvc_driver.c
> > index 70842c5..9f23dcf 100644
> > --- a/drivers/media/usb/uvc/uvc_driver.c
> > +++ b/drivers/media/usb/uvc/uvc_driver.c
> > @@ -1880,6 +1880,7 @@ static void uvc_unregister_video(struct uvc_device
> > *dev)
> >  			continue;
> >  
> >  		video_unregister_device(&stream->vdev);
> > +		video_unregister_device(&stream->meta.vdev);
> >  
> >  		uvc_debugfs_cleanup_stream(stream);
> >  	}
> > @@ -1941,6 +1942,9 @@ static int uvc_register_video(struct uvc_device *dev,
> >  		return ret;
> >  	}
> >  
> > +	/* Register a metadata node. */
> > +	uvc_meta_register(stream);
> 
> This can fail, you should handle errors.

Yes, this is in a way deliberate. If metadata node registration fails, the 
driver continues without one. Would you prefer to fail and unregister the 
main node in this case?

> >  	if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> >  		stream->chain->caps |= V4L2_CAP_VIDEO_CAPTURE;
> >  	else
> 
> [snip]
> 
> > diff --git a/drivers/media/usb/uvc/uvc_metadata.c 
> b/drivers/media/usb/uvc/uvc_metadata.c
> > new file mode 100644
> > index 0000000..876badd
> > --- /dev/null
> > +++ b/drivers/media/usb/uvc/uvc_metadata.c
> > @@ -0,0 +1,158 @@
> > +/*
> > + *      uvc_metadata.c  --  USB Video Class driver - Metadata handling
> > + *
> > + *      Copyright (C) 2016
> > + *          Guennadi Liakhovetski (guennadi.liakhovetski@xxxxxxxxx)
> > + *
> > + *      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 <linux/kernel.h>
> > +#include <linux/list.h>
> > +#include <linux/module.h>
> > +#include <linux/usb.h>
> > +#include <linux/videodev2.h>
> > +
> > +#include <media/v4l2-ioctl.h>
> > +#include <media/videobuf2-v4l2.h>
> > +#include <media/videobuf2-vmalloc.h>
> > +
> > +#include "uvcvideo.h"
> > +
> > +/* -----------------------------------------------------------------------
> > + * videobuf2 Queue Operations
> > + */
> > +
> > +static struct vb2_ops uvc_meta_queue_ops = {
> > +	.queue_setup = uvc_queue_setup,
> > +	.buf_prepare = uvc_buffer_prepare,
> > +	.buf_queue = uvc_buffer_queue,
> > +	.wait_prepare = vb2_ops_wait_prepare,
> > +	.wait_finish = vb2_ops_wait_finish,
> > +	.stop_streaming = uvc_stop_streaming,
> 
> This looks unbalanced without a start_streaming operation. I know that
> you've modified the uvc_stop_streaming() function to not stop the video
> stream for metadata video nodes, but I think the code would be clearer
> if you implemented a uvc_meta_stop_streaming() function 
> for 

Unfinished sentence? Adding a uvc_meta_stop_streaming() wouldn't balance 
the .start_streaming() by itself. Do you want a dummy function, returning 
0 there? Seems even uglier to me...

> > +};
> > +
> > +/* ------------------------------------------------------------------------
> > + * V4L2 ioctls
> > + */
> > +
> > +static int meta_v4l2_querycap(struct file *file, void *fh,
> > +			      struct v4l2_capability *cap)
> 
> For consistency with the rest of the code, could you prefix this and the
> next function with uvc_ ?
> 
> > +{
> > +	struct v4l2_fh *vfh = file->private_data;
> > +	struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
> > +
> > +	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));
> > +
> > +	return 0;
> > +}
> 
> [snip]
> 
> > +int uvc_meta_register(struct uvc_streaming *stream)
> > +{
> > +	struct uvc_device *dev = stream->dev;
> > +	struct uvc_meta_device *meta = &stream->meta;
> > +	struct video_device *vdev = &meta->vdev;
> > +	struct uvc_video_queue *quvc = &meta->queue;
> > +	struct vb2_queue *queue = &quvc->queue;
> > +	int ret;
> > +
> > +	mutex_init(&quvc->mutex);
> > +	spin_lock_init(&quvc->irqlock);
> > +	INIT_LIST_HEAD(&quvc->irqqueue);
> > +
> > +	/*
> > +	 * We register metadata device nodes only if the METADATA_NODE quirk
> > is
> > +	 * set and only on interfaces with bulk endpoints. To meaningfully
> > +	 * support interfaces with isochronous endpoints, we need to collect
> > +	 * headers from all payloads, comprising a single frame. For that we
> > +	 * need to know the maximum number of such payloads per frame to be
> > able
> > +	 * to calculate the buffer size. Currently this information is
> > +	 * unavailable. A proposal should be made to the UVC committee to add
> > +	 * this information to camera descriptors.
> > +	 */
> > +	if (!(dev->quirks & UVC_QUIRK_METADATA_NODE) ||
> > +	    dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
> > +		return 0;
> > +
> > +	vdev->v4l2_dev = &dev->vdev;
> > +	vdev->fops = &uvc_meta_fops;
> > +	vdev->ioctl_ops = &uvc_meta_ioctl_ops;
> > +	vdev->release = video_device_release_empty;
> 
> Please, no. You'll end up crashing the kernel if an application has the meta
> devnode open when the device is disconnected. Proper refcounting is needed.

Hm, I just tried

open("/dev/video1",..);
sleep();
/* unplug */
ioctl();
close();

no crash. Why do you think there should be one? Doesn't the 
uvc_disconnect() already take care of everything, including unregistering 
both video devices?

> > +	vdev->prio = &stream->chain->prio;
> > +	vdev->vfl_dir = VFL_DIR_RX;
> > +	vdev->queue = queue;
> > +	vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
> > +	strlcpy(vdev->name, dev->name, sizeof(vdev->name));
> > +
> > +	video_set_drvdata(vdev, stream);
> > +
> > +	/* Initialize the video buffer queue. */
> > +	queue->type = V4L2_BUF_TYPE_META_CAPTURE;
> > +	queue->io_modes = VB2_MMAP | VB2_USERPTR;
> > +	queue->drv_priv = quvc;
> > +	queue->buf_struct_size = sizeof(struct uvc_buffer);
> > +	queue->ops = &uvc_meta_queue_ops;
> > +	queue->mem_ops = &vb2_vmalloc_memops;
> > +	queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
> > +		| V4L2_BUF_FLAG_TSTAMP_SRC_SOE;
> > +	queue->lock = &quvc->mutex;
> > +	ret = vb2_queue_init(queue);
> > +	if (ret < 0)
> > +		return ret;
> 
> How about calling uvc_queue_init() instead of duplicating the code ? You would
> just need to extend the function with a parameter for the queue ops. Or,
> possibly even better, you could move the definition of the queue ops to
> uvc_queue.c. That way you won't have to expose a bunch of currently static
> queue functions.

I tried to isolate all the metadata node related code to a separate file 
to avoid blowing up existing code paths and for easier maintenance, but 
sure, can do.

> Similarly, it looks like you're duplicating code from uvc_register_video(),
> which I believe could be shared.

It could, yes, but that would only save about 5 to 10 lines of code, would 
not put a complete vdev initialisation in one place or would have to 
explicitly check for META. This looks less suitable for reuse to me.

> > +	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
> > +	if (ret < 0)
> > +		uvc_printk(KERN_ERR, "Failed to register metadata device (%d).
> \n", ret);
> 
> This line is over 80 characters.
> 
> > +	return ret;
> > +}
> > diff --git a/drivers/media/usb/uvc/uvc_queue.c
> > b/drivers/media/usb/uvc/uvc_queue.c
> > index aa21997..77dedbc 100644
> > --- a/drivers/media/usb/uvc/uvc_queue.c
> > +++ b/drivers/media/usb/uvc/uvc_queue.c
> > @@ -74,13 +74,26 @@ static void uvc_queue_return_buffers(struct
> > uvc_video_queue *queue,
> >   * videobuf2 queue operations
> >   */
> >  
> > -static int uvc_queue_setup(struct vb2_queue *vq,
> > -			   unsigned int *nbuffers, unsigned int *nplanes,
> > -			   unsigned int sizes[], struct device *alloc_devs[])
> > +int uvc_queue_setup(struct vb2_queue *vq,
> > +		    unsigned int *nbuffers, unsigned int *nplanes,
> > +		    unsigned int sizes[], struct device *alloc_devs[])
> >  {
> >  	struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
> > -	struct uvc_streaming *stream = uvc_queue_to_stream(queue);
> > -	unsigned size = stream->ctrl.dwMaxVideoFrameSize;
> > +	struct uvc_streaming *stream;
> > +	unsigned int size;
> > +
> > +	switch (vq->type) {
> > +	case V4L2_BUF_TYPE_META_CAPTURE:
> > +		size = UVC_METATADA_BUF_SIZE;
> > +
> > +		if (*nplanes && *nplanes != 1)
> > +			return -EINVAL;
> 
> As the driver doesn't support any multi-plane format, should this check be
> moved out of the switch ? You should then add it in a separate patch before
> this one.

I've double-checked the documentation for struct vb2_ops and it says:

 *			The driver should return the required number of buffers
 *			in \*num_buffers, the required number of planes per
 *			buffer in \*num_planes,...

But later

 *					If either \*num_planes or the requested
 *			sizes are invalid callback must return %-EINVAL.

I'd say, the former refers to the case, when *num_planes = 0 on input, so, 
yes, we can add a check globally.

> > +		break;
> 
> Could you add a blank line between break statements and the next case to be
> consistent with the driver's coding style ?
> 
> > +	default:
> > +		stream = uvc_queue_to_stream(queue);
> > +		size = stream->ctrl.dwMaxVideoFrameSize;
> 
> While not strictly needed, the driver uses break statements in the default
> cases. Could you please do so here (and below) too ?
> 
> > +	}
> >  
> >  	/* Make sure the image size is large enough. */
> >  	if (*nplanes)
> > @@ -90,34 +103,47 @@ static int uvc_queue_setup(struct vb2_queue *vq,
> >  	return 0;
> >  }
> >  
> > -static int uvc_buffer_prepare(struct vb2_buffer *vb)
> > +int uvc_buffer_prepare(struct vb2_buffer *vb)
> >  {
> >  	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> >  	struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
> >  	struct uvc_buffer *buf = uvc_vbuf_to_buffer(vbuf);
> >  
> > -	if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
> > -	    vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
> > -		uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds
> > .\n");
> > -		return -EINVAL;
> > -	}
> > -
> >  	if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED))
> >  		return -ENODEV;
> >  
> > +	switch (vb->type) {
> > +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> > +		if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
> > +			uvc_trace(UVC_TRACE_CAPTURE,
> > +				  "[E] Bytes used out of bounds.\n");
> > +			return -EINVAL;
> > +		}
> > +
> > +		buf->bytesused = vb2_get_plane_payload(vb, 0);
> > +
> > +		break;
> > +	case V4L2_BUF_TYPE_META_CAPTURE:
> > +		if (vb->num_planes != 1 ||
> > +		    vb2_plane_size(vb, 0) < UVC_METATADA_BUF_SIZE) {
> 
> Can this happen, given that queue_setup rejects buffers that verify those
> conditions ?

As far as I can see, e.g. in the USERPTR case the length is directly 
copied from the user-provided struct v4l2_buffer:

vb2_qbuf()
vb2_core_qbuf()
__buf_prepare()
__prepare_userptr()	
__fill_vb2_buffer()
	planes[0].length = b->length;

Still, the length check is optional. We could just as well omit it here 
and rely on the respective uvc_video_decode_*() function to limit the 
amount of copied data. But it does look like vb->num_planes cannot be 
wrong, you're right about that one.

> > +			uvc_trace(UVC_TRACE_CAPTURE,
> > +				  "[E] Invalid buffer configuration.\n");
> > +			return -EINVAL;
> > +		}
> > +		/* Fall through */
> > +	default:
> > +		buf->bytesused = 0;
> > +	}
> > +
> >  	buf->state = UVC_BUF_STATE_QUEUED;
> >  	buf->error = 0;
> >  	buf->mem = vb2_plane_vaddr(vb, 0);
> >  	buf->length = vb2_plane_size(vb, 0);
> > -	if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> 
> Unless I was mistaken in my previous comment, I think you only need to change
> the condition to vb->type != V4L2_BUF_TYPE_VIDEO_OUTPUT, no other change to
> the function should be needed.

Yeah, ok, let's do that.

> > -		buf->bytesused = 0;
> > -	else
> > -		buf->bytesused = vb2_get_plane_payload(vb, 0);
> >  
> >  	return 0;
> >  }
> >  
> > -static void uvc_buffer_queue(struct vb2_buffer *vb)
> > +void uvc_buffer_queue(struct vb2_buffer *vb)
> >  {
> >  	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
> >  	struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
> > @@ -169,13 +195,15 @@ static int uvc_start_streaming(struct vb2_queue *vq,
> > unsigned int count)
> >  	return ret;
> >  }
> >  
> > -static void uvc_stop_streaming(struct vb2_queue *vq)
> > +void uvc_stop_streaming(struct vb2_queue *vq)
> >  {
> >  	struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
> > -	struct uvc_streaming *stream = uvc_queue_to_stream(queue);
> >  	unsigned long flags;
> >  
> > -	uvc_video_enable(stream, 0);
> > +	if (vq->type != V4L2_BUF_TYPE_META_CAPTURE) {
> > +		struct uvc_streaming *stream = uvc_queue_to_stream(queue);
> 
> Hasn't checkpatch complained about a missing blank line here ?
> 
> > +		uvc_video_enable(stream, 0);
> > +	}
> >  
> >  	spin_lock_irqsave(&queue->irqlock, flags);
> >  	uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
> > diff --git a/drivers/media/usb/uvc/uvc_video.c
> > b/drivers/media/usb/uvc/uvc_video.c
> > index fb86d6a..3a54439 100644
> > --- a/drivers/media/usb/uvc/uvc_video.c
> > +++ b/drivers/media/usb/uvc/uvc_video.c
> > @@ -1152,8 +1152,76 @@ static void uvc_video_validate_buffer(const struct
> > uvc_streaming *stream,
> >  /*
> >   * Completion handler for video URBs.
> >   */
> 
> I think this function would be better placed in a new "Metadata" section right
> before the "URB handling" section.
> 
> > +
> > +static void uvc_video_decode_meta(struct uvc_streaming *stream,
> > +			struct uvc_buffer *buf, struct uvc_buffer *meta_buf,
> 
> You might want to call buf video_buf for clarity.
> 
> > +			u8 *mem, unsigned int length)
> > +{
> > +	struct uvc_meta_buf *meta;
> > +	size_t len_std = 2;
> > +	bool has_pts, has_scr;
> > +	u8 *scr;
> > +
> > +	if (!meta_buf || length == 2 ||
> > +	    meta_buf->length - meta_buf->bytesused <
> > +	    length + sizeof(meta->ts) + sizeof(meta->sof) ||
> > +	    length != mem[0])
> 
> I don't think the last condition can occur here, as it should be caught in
> uvc_video_decode_start() and result in the payload being skipped.
> 
> > +		return;
> > +
> > +	has_pts = mem[1] & UVC_STREAM_PTS;
> > +	has_scr = mem[1] & UVC_STREAM_SCR;
> > +
> > +	if (has_pts) {
> > +		len_std += 4;
> > +		scr = mem + 6;
> > +	} else {
> > +		scr = mem + 2;
> > +	}
> > +
> > +	if (has_scr)
> > +		len_std += 6;
> > +
> > +	if (length == len_std && (!has_scr ||
> > +				  !memcmp(scr, stream->clock.last_scr, 6)))
> > +		return;
> 
> I think a comment would be useful to explain the strategy. I understand it now
> as we've discussed it recently, but I'm not sure I still will in 6 months :-)
> You could add a comment block at the beginning of the function, similarly to
> uvc_video_decode_start().
> 
> > +
> > +	meta = (struct uvc_meta_buf *)((u8 *)meta_buf->mem + meta_buf
> > ->bytesused);
> > +	uvc_video_get_ts(&meta->ts);
> > +	meta->sof = usb_get_current_frame_number(stream->dev->udev);
> 
> You should disable local interrupts around those two calls to ensure that they
> will be as close as possible.

Hmmm, is it a "recommended" practice? But sure, it can indeed help, so, if 
you're ok with it, will do.

> > +
> > +	if (!meta_buf->bytesused) {
> > +		meta_buf->buf.sequence = buf->buf.sequence;
> > +		meta_buf->buf.field = buf->buf.field;
> > +		meta_buf->buf.vb2_buf.timestamp = buf->buf.vb2_buf.timestamp;
> 
> I wonder whether it wouldn't be clearer to do this in
> uvc_video_step_buffers(). You wouldn't need to check bytesused then, and the
> code would be more future-proof in case the driver gets changed to set one of
> those fields later than uvc_video_decode_start().
> 
> > +	}
> > +
> > +	if (has_scr)
> > +		memcpy(stream->clock.last_scr, scr, 6);
> > +
> > +	memcpy(&meta->length, mem, length);
> > +	meta_buf->bytesused += length + sizeof(meta->ts) + sizeof(meta->sof);
> > +
> > +	uvc_trace(UVC_TRACE_FRAME,
> > +		  "%s(): t-sys %lu.%09lus, SOF %u, len %u, flags 0x%x, PTS %u,
> > STC %u frame SOF %u\n",
> > +		  __func__, meta->ts.tv_sec, meta->ts.tv_nsec, meta->sof,
> > +		  meta->length, meta->flags, has_pts ? *(u32 *)meta->buf : 0,
> > +		  has_scr ? *(u32 *)scr : 0,
> > +		  has_scr ? *(u32 *)(scr + 4) & 0x7ff : 0);
> > +}
> > +
> > +static struct uvc_buffer *uvc_video_step_buffers(struct uvc_streaming
> > *stream,
> > +			struct uvc_buffer *buf, struct uvc_buffer **meta_buf)
> 
> Wouldn't it better to make the function more symmetrical by passing **buf the
> same way we pass **meta_buf ?

No! I definitely don't think symmetry is worth it!

> And as commented above, I think I would rename
> buf to video_buf.
> 
> > +{
> > +	if (*meta_buf) {
> > +		(*meta_buf)->state = UVC_BUF_STATE_READY;
> > +		*meta_buf = uvc_queue_next_buffer(&stream->meta.queue,
> > +						  *meta_buf);
> > +	}
> > +	return uvc_queue_next_buffer(&stream->queue, buf);
> > +}
> > +
> >  static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming
> >  *stream,
> > -	struct uvc_buffer *buf)
> > +			struct uvc_buffer *buf, struct uvc_buffer *meta_buf)
> >  {
> >  	u8 *mem;
> >  	int ret, i;
> 
> [snip]
> 
> > diff --git a/drivers/media/usb/uvc/uvcvideo.h 
> b/drivers/media/usb/uvc/uvcvideo.h
> > index 15e415e..d06e2d6 100644
> > --- a/drivers/media/usb/uvc/uvcvideo.h
> > +++ b/drivers/media/usb/uvc/uvcvideo.h
> > @@ -185,6 +185,7 @@
> >  #define UVC_QUIRK_RESTRICT_FRAME_RATE	0x00000200
> >  #define UVC_QUIRK_RESTORE_CTRLS_ON_INIT	0x00000400
> >  #define UVC_QUIRK_FORCE_Y8		0x00000800
> > +#define UVC_QUIRK_METADATA_NODE		0x00001000
> 
> I notice that you don't set the quirk for any device. What's your plan there,
> will there be more patches ?

Yes, if the quirk is your preferred way to go, I can submit (an) 
additional patch(es) with some cameras.

> >  /* Format flags */
> >  #define UVC_FMT_FLAG_COMPRESSED		0x00000001
> 
> [snip]
> 
> > @@ -546,6 +556,8 @@ struct uvc_streaming {
> >  		u16 last_sof;
> >  		u16 sof_offset;
> >  
> > +		u8 last_scr[6];
> > +
> >  		spinlock_t lock;
> >  	} clock;
> >  };
> > @@ -684,6 +696,12 @@ extern unsigned long uvc_queue_get_unmapped_area(struct 
> uvc_video_queue *queue,
> >  		unsigned long pgoff);
> >  #endif
> >  extern int uvc_queue_allocated(struct uvc_video_queue *queue);
> > +extern int uvc_queue_setup(struct vb2_queue *vq,
> > +			   unsigned int *nbuffers, unsigned int *nplanes,
> > +			   unsigned int sizes[], struct device *alloc_devs[]);
> > +extern int uvc_buffer_prepare(struct vb2_buffer *vb);
> > +extern void uvc_buffer_queue(struct vb2_buffer *vb);
> > +extern void uvc_stop_streaming(struct vb2_queue *vq);
> 
> You can drop the extern keyword, it's not needed.

I know, but I was following the coding style of this driver ;-) But in 
fact after moving uvc_meta_queue_qops to uvc_queue.c, these declarations 
aren't needed any more, which is good.

> >  static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
> >  {
> >  	return vb2_is_streaming(&queue->queue);
> 
> [snip]
> 
> > diff --git a/include/uapi/linux/uvcvideo.h b/include/uapi/linux/uvcvideo.h
> > index 3b08186..c503dcc 100644
> > --- a/include/uapi/linux/uvcvideo.h
> > +++ b/include/uapi/linux/uvcvideo.h
> > @@ -67,4 +67,23 @@ struct uvc_xu_control_query {
> >  #define UVCIOC_CTRL_MAP		_IOWR('u', 0x20, struct 
> uvc_xu_control_mapping)
> >  #define UVCIOC_CTRL_QUERY	_IOWR('u', 0x21, struct uvc_xu_control_query)
> >  
> > +/*
> > + * Metadata node
> > + */
> > +
> > +/*
> 
> Could you please use kerneldoc comments to document this structure ?
> 
> > + * UVC metadata nodes fill buffers with possibly multiple instances of the
> > + * following struct. The first two fields are added by the driver, they can
> > be
> > + * used for clock synchronisation. The rest is an exact copy of a UVC
> > payload
> > + * header. Only complete objects with complete buffers are included.
> > Therefore
> > + * it's always sizeof(meta->ts) + sizeof(meta->sof) + meta->length bytes
> > large.
> > + */
> > +struct uvc_meta_buf {
> > +	struct timespec ts;
> 
> timespec has a different size on 32-bit and 64-bit architectures, so there
> could be issues on 32-bit userspace running on a 64-bit kernel.
> 
> Additionally, on 32-bit platforms, timespec is not year 2038-safe. I thought
> that timespec64 was exposed to userspace nowadays, but it doesn't seem to be
> the case. I'm not sure how to handle this.

Oh, that isn't good :-/ I'll have to think more about this. If you get any 
more ideas, I'd be glad to hear them too.

Thanks
Guennadi

> > +	__u16 sof;
> > +	__u8 length;
> > +	__u8 flags;
> > +	__u8 buf[];
> 
> I had imagined inserting the ts + sof after the standard header, but this
> probably makes more sense.
> 
> > +};
> > +
> >  #endif
> > diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
> > index 45cf735..9b0cc7e 100644
> > --- a/include/uapi/linux/videodev2.h
> > +++ b/include/uapi/linux/videodev2.h
> > @@ -683,6 +683,9 @@ struct v4l2_pix_format {
> >  #define V4L2_META_FMT_VSP1_HGO    v4l2_fourcc('V', 'S', 'P', 'H') /* R-Car
> >  VSP1 1-D Histogram */
> >  #define V4L2_META_FMT_VSP1_HGT    v4l2_fourcc('V', 'S', 'P', 'T') /* R-Car
> >  VSP1 2-D Histogram */
> >  
> > +/* Meta-data formats */
> > +#define V4L2_META_FMT_UVC         v4l2_fourcc('U', 'V', 'C', 'H') /* UVC 
> Payload Header metadata */
> 
> This should be documented in Documentation/media/, and probably split to a
> separate patch.
> 
> > +
> >  /* priv field value to indicates that subsequent fields are valid. */
> >  #define V4L2_PIX_FMT_PRIV_MAGIC		0xfeedcafe
> >  
> 
> -- 
> Regards,
> 
> Laurent Pinchart
> 



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux