Re: [PATCH V3 3/5] usb: gadget/uvc: Port UVC webcam gadget to use videobuf2 framework

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

 



Hi Bhupesh,

On Thu, Jan 17, 2013 at 04:23:51PM +0530, Bhupesh Sharma wrote:
> This patch reworks the videobuffer management logic present in the UVC
> webcam gadget and ports it to use the "more apt" videobuf2 framework for
> video buffer management.
> 
> To support routing video data captured from a real V4L2 video capture
> device with a "zero copy" operation on videobuffers (as they pass from
> the V4L2 domain to UVC domain via a user-space application), we need to
> support USER_PTR IO method at the UVC gadget side.
> 
> So the V4L2 capture device driver can still continue to use MMAP IO
> method and now the user-space application can just pass a pointer to the
> video buffers being dequeued from the V4L2 device side while queueing
> them at the UVC gadget end. This ensures that we have a "zero-copy"
> design as the videobuffers pass from the V4L2 capture device to the UVC
> gadget.
> 
> Note that there will still be a need to apply UVC specific payload
> headers on top of each UVC payload data, which will still require a copy
> operation to be performed in the 'encode' routines of the UVC gadget.
> 
> This patch also addresses one issue found out while porting the UVC
> gadget to videobuf2 framework:
> 	- In case the usb requests queued by the gadget get completed
> 	  with a status of -ESHUTDOWN (disconnected from host),
> 	  the queue of videobuf2 should be cancelled to ensure that the
> 	  application space daemon is not left in a state waiting for
> 	  a vb2 to be successfully absorbed at the USB side.
> 
> Signed-off-by: Bhupesh Sharma <bhupesh.sharma@xxxxxx>
> ---
>  drivers/usb/gadget/Kconfig     |    1 +
>  drivers/usb/gadget/uvc_queue.c |  537 ++++++++++++----------------------------
>  drivers/usb/gadget/uvc_queue.h |   25 +--
>  drivers/usb/gadget/uvc_v4l2.c  |   27 +--
>  drivers/usb/gadget/uvc_video.c |   18 +-
>  5 files changed, 189 insertions(+), 419 deletions(-)
> 
> diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
> index e0ff51b..b17ee25 100644
> --- a/drivers/usb/gadget/Kconfig
> +++ b/drivers/usb/gadget/Kconfig
> @@ -952,6 +952,7 @@ endif
>  config USB_G_WEBCAM
>  	tristate "USB Webcam Gadget"
>  	depends on VIDEO_DEV
> +	select VIDEOBUF2_VMALLOC
>  	select USB_LIBCOMPOSITE
>  	help
>  	  The Webcam Gadget acts as a composite USB Audio and Video Class
> diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c
> index 104ae9c..bd20fab 100644
> --- a/drivers/usb/gadget/uvc_queue.c
> +++ b/drivers/usb/gadget/uvc_queue.c
> @@ -10,6 +10,7 @@
>   *	(at your option) any later version.
>   */
>  
> +#include <linux/atomic.h>
>  #include <linux/kernel.h>
>  #include <linux/mm.h>
>  #include <linux/list.h>
> @@ -18,7 +19,8 @@
>  #include <linux/videodev2.h>
>  #include <linux/vmalloc.h>
>  #include <linux/wait.h>
> -#include <linux/atomic.h>
> +
> +#include <media/videobuf2-vmalloc.h>
>  
>  #include "uvc.h"
>  
> @@ -28,330 +30,169 @@
>   * Video queues is initialized by uvc_queue_init(). The function performs
>   * basic initialization of the uvc_video_queue struct and never fails.
>   *
> - * Video buffer allocation and freeing are performed by uvc_alloc_buffers and
> - * uvc_free_buffers respectively. The former acquires the video queue lock,
> - * while the later must be called with the lock held (so that allocation can
> - * free previously allocated buffers). Trying to free buffers that are mapped
> - * to user space will return -EBUSY.
> - *
> - * Video buffers are managed using two queues. However, unlike most USB video
> - * drivers that use an in queue and an out queue, we use a main queue to hold
> - * all queued buffers (both 'empty' and 'done' buffers), and an irq queue to
> - * hold empty buffers. This design (copied from video-buf) minimizes locking
> - * in interrupt, as only one queue is shared between interrupt and user
> - * contexts.
> - *
> - * Use cases
> - * ---------
> - *
> - * Unless stated otherwise, all operations that modify the irq buffers queue
> - * are protected by the irq spinlock.
> - *
> - * 1. The user queues the buffers, starts streaming and dequeues a buffer.
> - *
> - *    The buffers are added to the main and irq queues. Both operations are
> - *    protected by the queue lock, and the later is protected by the irq
> - *    spinlock as well.
> - *
> - *    The completion handler fetches a buffer from the irq queue and fills it
> - *    with video data. If no buffer is available (irq queue empty), the handler
> - *    returns immediately.
> - *
> - *    When the buffer is full, the completion handler removes it from the irq
> - *    queue, marks it as ready (UVC_BUF_STATE_DONE) and wakes its wait queue.
> - *    At that point, any process waiting on the buffer will be woken up. If a
> - *    process tries to dequeue a buffer after it has been marked ready, the
> - *    dequeing will succeed immediately.
> - *
> - * 2. Buffers are queued, user is waiting on a buffer and the device gets
> - *    disconnected.
> - *
> - *    When the device is disconnected, the kernel calls the completion handler
> - *    with an appropriate status code. The handler marks all buffers in the
> - *    irq queue as being erroneous (UVC_BUF_STATE_ERROR) and wakes them up so
> - *    that any process waiting on a buffer gets woken up.
> - *
> - *    Waking up up the first buffer on the irq list is not enough, as the
> - *    process waiting on the buffer might restart the dequeue operation
> - *    immediately.
> - *
> + * Video buffers are managed by videobuf2. The driver uses a mutex to protect
> + * the videobuf2 queue operations by serializing calls to videobuf2 and a
> + * spinlock to protect the IRQ queue that holds the buffers to be processed by
> + * the driver.
>   */
>  
> -static void
> -uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
> -{
> -	mutex_init(&queue->mutex);
> -	spin_lock_init(&queue->irqlock);
> -	INIT_LIST_HEAD(&queue->mainqueue);
> -	INIT_LIST_HEAD(&queue->irqqueue);
> -	queue->type = type;
> -}
> -
> -/*
> - * Free the video buffers.
> - *
> - * This function must be called with the queue lock held.
> +/* -----------------------------------------------------------------------------
> + * videobuf2 queue operations
>   */
> -static int uvc_free_buffers(struct uvc_video_queue *queue)
> +
> +static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
> +			   unsigned int *nbuffers, unsigned int *nplanes,
> +			   unsigned int sizes[], void *alloc_ctxs[])
>  {
> -	unsigned int i;
> +	struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
> +	struct uvc_video *video = container_of(queue, struct uvc_video, queue);
>  
> -	for (i = 0; i < queue->count; ++i) {
> -		if (queue->buffer[i].vma_use_count != 0)
> -			return -EBUSY;
> -	}
> +	if (*nbuffers > UVC_MAX_VIDEO_BUFFERS)
> +		*nbuffers = UVC_MAX_VIDEO_BUFFERS;
>  
> -	if (queue->count) {
> -		vfree(queue->mem);
> -		queue->count = 0;
> -	}
> +	*nplanes = 1;
> +
> +	sizes[0] = video->imagesize;
>  
>  	return 0;
>  }
>  
> -/*
> - * Allocate the video buffers.
> - *
> - * Pages are reserved to make sure they will not be swapped, as they will be
> - * filled in the URB completion handler.
> - *
> - * Buffers will be individually mapped, so they must all be page aligned.
> - */
> -static int
> -uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers,
> -		  unsigned int buflength)
> +static int uvc_buffer_prepare(struct vb2_buffer *vb)
>  {
> -	unsigned int bufsize = PAGE_ALIGN(buflength);
> -	unsigned int i;
> -	void *mem = NULL;
> -	int ret;
> +	struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
> +	struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
>  
> -	if (nbuffers > UVC_MAX_VIDEO_BUFFERS)
> -		nbuffers = UVC_MAX_VIDEO_BUFFERS;
> +	if (vb->v4l2_buf.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;
> +	}
>  
> -	mutex_lock(&queue->mutex);
> +	if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED))
> +		return -ENODEV;
>  
> -	if ((ret = uvc_free_buffers(queue)) < 0)
> -		goto done;
> +	buf->state = UVC_BUF_STATE_QUEUED;
> +	buf->mem = vb2_plane_vaddr(vb, 0);
> +	buf->length = vb2_plane_size(vb, 0);
> +	if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		buf->bytesused = 0;
> +	else
> +		buf->bytesused = vb2_get_plane_payload(vb, 0);
>  
> -	/* Bail out if no buffers should be allocated. */
> -	if (nbuffers == 0)
> -		goto done;
> +	return 0;
> +}
>  
> -	/* Decrement the number of buffers until allocation succeeds. */
> -	for (; nbuffers > 0; --nbuffers) {
> -		mem = vmalloc_32(nbuffers * bufsize);
> -		if (mem != NULL)
> -			break;
> -	}
> +static void uvc_buffer_queue(struct vb2_buffer *vb)
> +{
> +	struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
> +	struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
> +	unsigned long flags;
>  
> -	if (mem == NULL) {
> -		ret = -ENOMEM;
> -		goto done;
> -	}
> +	spin_lock_irqsave(&queue->irqlock, flags);
>  
> -	for (i = 0; i < nbuffers; ++i) {
> -		memset(&queue->buffer[i], 0, sizeof queue->buffer[i]);
> -		queue->buffer[i].buf.index = i;
> -		queue->buffer[i].buf.m.offset = i * bufsize;
> -		queue->buffer[i].buf.length = buflength;
> -		queue->buffer[i].buf.type = queue->type;
> -		queue->buffer[i].buf.sequence = 0;
> -		queue->buffer[i].buf.field = V4L2_FIELD_NONE;
> -		queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP;
> -		queue->buffer[i].buf.flags = 0;
> -		init_waitqueue_head(&queue->buffer[i].wait);
> +	if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) {
> +		list_add_tail(&buf->queue, &queue->irqqueue);
> +	} else {
> +		/* If the device is disconnected return the buffer to userspace
> +		 * directly. The next QBUF call will fail with -ENODEV.
> +		 */
> +		buf->state = UVC_BUF_STATE_ERROR;
> +		vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
>  	}
>  
> -	queue->mem = mem;
> -	queue->count = nbuffers;
> -	queue->buf_size = bufsize;
> -	ret = nbuffers;
> -
> -done:
> -	mutex_unlock(&queue->mutex);
> -	return ret;
> +	spin_unlock_irqrestore(&queue->irqlock, flags);
>  }
>  
> -static void __uvc_query_buffer(struct uvc_buffer *buf,
> -		struct v4l2_buffer *v4l2_buf)
> -{
> -	memcpy(v4l2_buf, &buf->buf, sizeof *v4l2_buf);
> -
> -	if (buf->vma_use_count)
> -		v4l2_buf->flags |= V4L2_BUF_FLAG_MAPPED;
> -
> -	switch (buf->state) {
> -	case UVC_BUF_STATE_ERROR:
> -	case UVC_BUF_STATE_DONE:
> -		v4l2_buf->flags |= V4L2_BUF_FLAG_DONE;
> -		break;
> -	case UVC_BUF_STATE_QUEUED:
> -	case UVC_BUF_STATE_ACTIVE:
> -		v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED;
> -		break;
> -	case UVC_BUF_STATE_IDLE:
> -	default:
> -		break;
> -	}
> -}
> +static struct vb2_ops uvc_queue_qops = {
> +	.queue_setup = uvc_queue_setup,
> +	.buf_prepare = uvc_buffer_prepare,
> +	.buf_queue = uvc_buffer_queue,
> +};
>  
> -static int
> -uvc_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf)
> +static int uvc_queue_init(struct uvc_video_queue *queue,
> +			   enum v4l2_buf_type type)
>  {
> -	int ret = 0;
> +	int ret;
>  
> -	mutex_lock(&queue->mutex);
> -	if (v4l2_buf->index >= queue->count) {
> -		ret = -EINVAL;
> -		goto done;
> -	}
> +	queue->queue.type = type;
> +	queue->queue.io_modes = VB2_MMAP | VB2_USERPTR;
> +	queue->queue.drv_priv = queue;
> +	queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
> +	queue->queue.ops = &uvc_queue_qops;
> +	queue->queue.mem_ops = &vb2_vmalloc_memops;
> +	ret = vb2_queue_init(&queue->queue);
> +	if (ret)
> +		return ret;
>  
> -	__uvc_query_buffer(&queue->buffer[v4l2_buf->index], v4l2_buf);
> +	mutex_init(&queue->mutex);
> +	spin_lock_init(&queue->irqlock);
> +	INIT_LIST_HEAD(&queue->irqqueue);
>  
> -done:
> -	mutex_unlock(&queue->mutex);
> -	return ret;
> +	return 0;
>  }
>  
>  /*
> - * Queue a video buffer. Attempting to queue a buffer that has already been
> - * queued will return -EINVAL.
> + * Free the video buffers.
>   */
> -static int
> -uvc_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf)
> +static void uvc_free_buffers(struct uvc_video_queue *queue)
>  {
> -	struct uvc_buffer *buf;
> -	unsigned long flags;
> -	int ret = 0;
> -
> -	uvc_trace(UVC_TRACE_CAPTURE, "Queuing buffer %u.\n", v4l2_buf->index);
> -
> -	if (v4l2_buf->type != queue->type ||
> -	    v4l2_buf->memory != V4L2_MEMORY_MMAP) {
> -		uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
> -			"and/or memory (%u).\n", v4l2_buf->type,
> -			v4l2_buf->memory);
> -		return -EINVAL;
> -	}
> -
>  	mutex_lock(&queue->mutex);
> -	if (v4l2_buf->index >= queue->count) {
> -		uvc_trace(UVC_TRACE_CAPTURE, "[E] Out of range index.\n");
> -		ret = -EINVAL;
> -		goto done;
> -	}
> -
> -	buf = &queue->buffer[v4l2_buf->index];
> -	if (buf->state != UVC_BUF_STATE_IDLE) {
> -		uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state "
> -			"(%u).\n", buf->state);
> -		ret = -EINVAL;
> -		goto done;
> -	}
> -
> -	if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
> -	    v4l2_buf->bytesused > buf->buf.length) {
> -		uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
> -		ret = -EINVAL;
> -		goto done;
> -	}
> +	vb2_queue_release(&queue->queue);
> +	mutex_unlock(&queue->mutex);
> +}
>  
> -	if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
> -		buf->buf.bytesused = 0;
> -	else
> -		buf->buf.bytesused = v4l2_buf->bytesused;
> +/*
> + * Allocate the video buffers.
> + */
> +static int uvc_alloc_buffers(struct uvc_video_queue *queue,
> +			     struct v4l2_requestbuffers *rb)
> +{
> +	int ret;
>  
> -	spin_lock_irqsave(&queue->irqlock, flags);
> -	if (queue->flags & UVC_QUEUE_DISCONNECTED) {
> -		spin_unlock_irqrestore(&queue->irqlock, flags);
> -		ret = -ENODEV;
> -		goto done;
> -	}
> -	buf->state = UVC_BUF_STATE_QUEUED;
> +	mutex_lock(&queue->mutex);
> +	ret = vb2_reqbufs(&queue->queue, rb);
> +	mutex_unlock(&queue->mutex);
>  
> -	ret = (queue->flags & UVC_QUEUE_PAUSED) != 0;
> -	queue->flags &= ~UVC_QUEUE_PAUSED;
> +	return ret ? ret : rb->count;
> +}
>  
> -	list_add_tail(&buf->stream, &queue->mainqueue);
> -	list_add_tail(&buf->queue, &queue->irqqueue);
> -	spin_unlock_irqrestore(&queue->irqlock, flags);
> +static int uvc_query_buffer(struct uvc_video_queue *queue,
> +			    struct v4l2_buffer *buf)
> +{
> +	int ret;
>  
> -done:
> +	mutex_lock(&queue->mutex);
> +	ret = vb2_querybuf(&queue->queue, buf);
>  	mutex_unlock(&queue->mutex);
> +
>  	return ret;
>  }
>  
> -static int uvc_queue_waiton(struct uvc_buffer *buf, int nonblocking)
> +static int uvc_queue_buffer(struct uvc_video_queue *queue,
> +			    struct v4l2_buffer *buf)
>  {
> -	if (nonblocking) {
> -		return (buf->state != UVC_BUF_STATE_QUEUED &&
> -			buf->state != UVC_BUF_STATE_ACTIVE)
> -			? 0 : -EAGAIN;
> -	}
> +	int ret;
>  
> -	return wait_event_interruptible(buf->wait,
> -		buf->state != UVC_BUF_STATE_QUEUED &&
> -		buf->state != UVC_BUF_STATE_ACTIVE);
> +	mutex_lock(&queue->mutex);
> +	ret = vb2_qbuf(&queue->queue, buf);
> +	mutex_unlock(&queue->mutex);
> +

How is the UVC_QUEUE_PAUSED handling supposed to be handled here?
I see that this patch lost this hunk from uvc_queue_buffer():

        ret |= (queue->flags & uvc_queue_paused) != 0;
        queue->flags &= ~UVC_QUEUE_PAUSED;

> +	return ret;
>  }
>  
>  /*
>   * Dequeue a video buffer. If nonblocking is false, block until a buffer is
>   * available.
>   */
> -static int
> -uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *v4l2_buf,
> -		   int nonblocking)
> +static int uvc_dequeue_buffer(struct uvc_video_queue *queue,
> +			      struct v4l2_buffer *buf, int nonblocking)
>  {
> -	struct uvc_buffer *buf;
> -	int ret = 0;
> -
> -	if (v4l2_buf->type != queue->type ||
> -	    v4l2_buf->memory != V4L2_MEMORY_MMAP) {
> -		uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
> -			"and/or memory (%u).\n", v4l2_buf->type,
> -			v4l2_buf->memory);
> -		return -EINVAL;
> -	}
> +	int ret;
>  
>  	mutex_lock(&queue->mutex);
> -	if (list_empty(&queue->mainqueue)) {
> -		uvc_trace(UVC_TRACE_CAPTURE, "[E] Empty buffer queue.\n");
> -		ret = -EINVAL;
> -		goto done;
> -	}
> -
> -	buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream);
> -	if ((ret = uvc_queue_waiton(buf, nonblocking)) < 0)
> -		goto done;
> -
> -	uvc_trace(UVC_TRACE_CAPTURE, "Dequeuing buffer %u (%u, %u bytes).\n",
> -		buf->buf.index, buf->state, buf->buf.bytesused);
> -
> -	switch (buf->state) {
> -	case UVC_BUF_STATE_ERROR:
> -		uvc_trace(UVC_TRACE_CAPTURE, "[W] Corrupted data "
> -			"(transmission error).\n");
> -		ret = -EIO;
> -	case UVC_BUF_STATE_DONE:
> -		buf->state = UVC_BUF_STATE_IDLE;
> -		break;
> -
> -	case UVC_BUF_STATE_IDLE:
> -	case UVC_BUF_STATE_QUEUED:
> -	case UVC_BUF_STATE_ACTIVE:
> -	default:
> -		uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer state %u "
> -			"(driver bug?).\n", buf->state);
> -		ret = -EINVAL;
> -		goto done;
> -	}
> -
> -	list_del(&buf->stream);
> -	__uvc_query_buffer(buf, v4l2_buf);
> -
> -done:
> +	ret = vb2_dqbuf(&queue->queue, buf, nonblocking);
>  	mutex_unlock(&queue->mutex);
> +
>  	return ret;
>  }
>  
> @@ -361,103 +202,27 @@ done:
>   * This function implements video queue polling and is intended to be used by
>   * the device poll handler.
>   */
> -static unsigned int
> -uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
> -	       poll_table *wait)
> +static unsigned int uvc_queue_poll(struct uvc_video_queue *queue,
> +				   struct file *file, poll_table *wait)
>  {
> -	struct uvc_buffer *buf;
> -	unsigned int mask = 0;
> +	unsigned int ret;
>  
>  	mutex_lock(&queue->mutex);
> -	if (list_empty(&queue->mainqueue))
> -		goto done;
> -
> -	buf = list_first_entry(&queue->mainqueue, struct uvc_buffer, stream);
> -
> -	poll_wait(file, &buf->wait, wait);
> -	if (buf->state == UVC_BUF_STATE_DONE ||
> -	    buf->state == UVC_BUF_STATE_ERROR)
> -		mask |= POLLOUT | POLLWRNORM;
> -
> -done:
> +	ret = vb2_poll(&queue->queue, file, wait);
>  	mutex_unlock(&queue->mutex);
> -	return mask;
> -}
>  
> -/*
> - * VMA operations.
> - */
> -static void uvc_vm_open(struct vm_area_struct *vma)
> -{
> -	struct uvc_buffer *buffer = vma->vm_private_data;
> -	buffer->vma_use_count++;
> +	return ret;
>  }
>  
> -static void uvc_vm_close(struct vm_area_struct *vma)
> +static int uvc_queue_mmap(struct uvc_video_queue *queue,
> +			  struct vm_area_struct *vma)
>  {
> -	struct uvc_buffer *buffer = vma->vm_private_data;
> -	buffer->vma_use_count--;
> -}
> -
> -static struct vm_operations_struct uvc_vm_ops = {
> -	.open		= uvc_vm_open,
> -	.close		= uvc_vm_close,
> -};
> -
> -/*
> - * Memory-map a buffer.
> - *
> - * This function implements video buffer memory mapping and is intended to be
> - * used by the device mmap handler.
> - */
> -static int
> -uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma)
> -{
> -	struct uvc_buffer *uninitialized_var(buffer);
> -	struct page *page;
> -	unsigned long addr, start, size;
> -	unsigned int i;
> -	int ret = 0;
> -
> -	start = vma->vm_start;
> -	size = vma->vm_end - vma->vm_start;
> +	int ret;
>  
>  	mutex_lock(&queue->mutex);
> -
> -	for (i = 0; i < queue->count; ++i) {
> -		buffer = &queue->buffer[i];
> -		if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
> -			break;
> -	}
> -
> -	if (i == queue->count || size != queue->buf_size) {
> -		ret = -EINVAL;
> -		goto done;
> -	}
> -
> -	/*
> -	 * VM_IO marks the area as being an mmaped region for I/O to a
> -	 * device. It also prevents the region from being core dumped.
> -	 */
> -	vma->vm_flags |= VM_IO;
> -
> -	addr = (unsigned long)queue->mem + buffer->buf.m.offset;
> -	while (size > 0) {
> -		page = vmalloc_to_page((void *)addr);
> -		if ((ret = vm_insert_page(vma, start, page)) < 0)
> -			goto done;
> -
> -		start += PAGE_SIZE;
> -		addr += PAGE_SIZE;
> -		size -= PAGE_SIZE;
> -	}
> -
> -	vma->vm_ops = &uvc_vm_ops;
> -	vma->vm_private_data = buffer;
> -	uvc_vm_open(vma);
> -
> -done:
> +	ret = vb2_mmap(&queue->queue, vma);
>  	mutex_unlock(&queue->mutex);
> +
>  	return ret;
>  }
>  
> @@ -484,9 +249,10 @@ static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
>  				       queue);
>  		list_del(&buf->queue);
>  		buf->state = UVC_BUF_STATE_ERROR;
> -		wake_up(&buf->wait);
> +		vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
>  	}
> -	/* This must be protected by the irqlock spinlock to avoid race
> +	/*
> +	 * This must be protected by the irqlock spinlock to avoid race
>  	 * conditions between uvc_queue_buffer and the disconnection event that
>  	 * could result in an interruptible wait in uvc_dequeue_buffer. Do not
>  	 * blindly replace this logic by checking for the UVC_DEV_DISCONNECTED
> @@ -516,26 +282,34 @@ static void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
>   */
>  static int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
>  {
> -	unsigned int i;
> +	unsigned long flags;
>  	int ret = 0;
>  
>  	mutex_lock(&queue->mutex);
>  	if (enable) {
> -		if (uvc_queue_streaming(queue)) {
> -			ret = -EBUSY;
> +		ret = vb2_streamon(&queue->queue, queue->queue.type);
> +		if (ret < 0)
>  			goto done;
> -		}
> -		queue->sequence = 0;
> -		queue->flags |= UVC_QUEUE_STREAMING;
> +
>  		queue->buf_used = 0;
>  	} else {
> -		uvc_queue_cancel(queue, 0);
> -		INIT_LIST_HEAD(&queue->mainqueue);
> +		ret = vb2_streamoff(&queue->queue, queue->queue.type);
> +		if (ret < 0)
> +			goto done;
> +
> +		spin_lock_irqsave(&queue->irqlock, flags);
> +
> +		INIT_LIST_HEAD(&queue->irqqueue);
>  
> -		for (i = 0; i < queue->count; ++i)
> -			queue->buffer[i].state = UVC_BUF_STATE_IDLE;
> +		/*
> +		 * FIXME: We need to clear the DISCONNECTED flag to ensure that
> +		 * applications will be able to queue buffers for the next
> +		 * streaming run. However, clearing it here doesn't guarantee
> +		 * that the device will be reconnected in the meantime.
> +		 */
> +		queue->flags &= ~UVC_QUEUE_DISCONNECTED;
>  
> -		queue->flags &= ~UVC_QUEUE_STREAMING;
> +		spin_unlock_irqrestore(&queue->irqlock, flags);
>  	}
>  
>  done:
> @@ -544,15 +318,15 @@ done:
>  }
>  
>  /* called with &queue_irqlock held.. */
> -static struct uvc_buffer *
> -uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf)
> +static struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
> +						 struct uvc_buffer *buf)
>  {
>  	struct uvc_buffer *nextbuf;
>  
>  	if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
> -	    buf->buf.length != buf->buf.bytesused) {
> +	    buf->length != buf->bytesused) {
>  		buf->state = UVC_BUF_STATE_QUEUED;
> -		buf->buf.bytesused = 0;
> +		vb2_set_plane_payload(&buf->buf, 0, 0);
>  		return buf;
>  	}
>  
> @@ -563,10 +337,18 @@ uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf)
>  	else
>  		nextbuf = NULL;
>  
> -	buf->buf.sequence = queue->sequence++;
> -	do_gettimeofday(&buf->buf.timestamp);
> +	/*
> +	 * FIXME: with videobuf2, the sequence number or timestamp fields
> +	 * are valid only for video capture devices and the UVC gadget usually
> +	 * is a video output device. Keeping these until the specs are clear on
> +	 * this aspect.
> +	 */
> +	buf->buf.v4l2_buf.sequence = queue->sequence++;
> +	do_gettimeofday(&buf->buf.v4l2_buf.timestamp);
> +
> +	vb2_set_plane_payload(&buf->buf, 0, buf->bytesused);
> +	vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE);
>  
> -	wake_up(&buf->wait);
>  	return nextbuf;
>  }
>  
> @@ -582,4 +364,3 @@ static struct uvc_buffer *uvc_queue_head(struct uvc_video_queue *queue)
>  
>  	return buf;
>  }
> -
> diff --git a/drivers/usb/gadget/uvc_queue.h b/drivers/usb/gadget/uvc_queue.h
> index 1812a8e..47ad0b8 100644
> --- a/drivers/usb/gadget/uvc_queue.h
> +++ b/drivers/usb/gadget/uvc_queue.h
> @@ -6,6 +6,7 @@
>  #include <linux/kernel.h>
>  #include <linux/poll.h>
>  #include <linux/videodev2.h>
> +#include <media/videobuf2-core.h>
>  
>  /* Maximum frame size in bytes, for sanity checking. */
>  #define UVC_MAX_FRAME_SIZE	(16*1024*1024)
> @@ -25,14 +26,13 @@ enum uvc_buffer_state {
>  };
>  
>  struct uvc_buffer {
> -	unsigned long vma_use_count;
> -	struct list_head stream;
> -
> -	/* Touched by interrupt handler. */
> -	struct v4l2_buffer buf;
> +	struct vb2_buffer buf;
>  	struct list_head queue;
> -	wait_queue_head_t wait;
> +
>  	enum uvc_buffer_state state;
> +	void *mem;
> +	unsigned int length;
> +	unsigned int bytesused;
>  };
>  
>  #define UVC_QUEUE_STREAMING		(1 << 0)
> @@ -41,26 +41,21 @@ struct uvc_buffer {
>  #define UVC_QUEUE_PAUSED		(1 << 3)
>  
>  struct uvc_video_queue {
> -	enum v4l2_buf_type type;
> +	struct vb2_queue queue;
> +	struct mutex mutex;	/* Protects queue */
>  
> -	void *mem;
>  	unsigned int flags;
>  	__u32 sequence;
>  
> -	unsigned int count;
> -	unsigned int buf_size;
>  	unsigned int buf_used;
> -	struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS];
> -	struct mutex mutex;	/* protects buffers and mainqueue */
> -	spinlock_t irqlock;	/* protects irqqueue */
>  
> -	struct list_head mainqueue;
> +	spinlock_t irqlock;	/* Protects irqqueue */
>  	struct list_head irqqueue;
>  };
>  
>  static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
>  {
> -	return queue->flags & UVC_QUEUE_STREAMING;
> +	return vb2_is_streaming(&queue->queue);
>  }
>  
>  #endif /* __KERNEL__ */
> diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c
> index 2ca9386..134bfe5 100644
> --- a/drivers/usb/gadget/uvc_v4l2.c
> +++ b/drivers/usb/gadget/uvc_v4l2.c
> @@ -148,16 +148,13 @@ uvc_v4l2_release(struct file *file)
>  	uvc_function_disconnect(uvc);
>  
>  	uvc_video_enable(video, 0);
> -	mutex_lock(&video->queue.mutex);
> -	if (uvc_free_buffers(&video->queue) < 0)
> -		printk(KERN_ERR "uvc_v4l2_release: Unable to free "
> -				"buffers.\n");
> -	mutex_unlock(&video->queue.mutex);
> +	uvc_free_buffers(&video->queue);
>  
>  	file->private_data = NULL;
>  	v4l2_fh_del(&handle->vfh);
>  	v4l2_fh_exit(&handle->vfh);
>  	kfree(handle);
> +
>  	return 0;
>  }
>  
> @@ -192,7 +189,7 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
>  	{
>  		struct v4l2_format *fmt = arg;
>  
> -		if (fmt->type != video->queue.type)
> +		if (fmt->type != video->queue.queue.type)
>  			return -EINVAL;
>  
>  		return uvc_v4l2_get_format(video, fmt);
> @@ -202,7 +199,7 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
>  	{
>  		struct v4l2_format *fmt = arg;
>  
> -		if (fmt->type != video->queue.type)
> +		if (fmt->type != video->queue.queue.type)
>  			return -EINVAL;
>  
>  		return uvc_v4l2_set_format(video, fmt);
> @@ -213,16 +210,13 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
>  	{
>  		struct v4l2_requestbuffers *rb = arg;
>  
> -		if (rb->type != video->queue.type ||
> -		    rb->memory != V4L2_MEMORY_MMAP)
> +		if (rb->type != video->queue.queue.type)
>  			return -EINVAL;
>  
> -		ret = uvc_alloc_buffers(&video->queue, rb->count,
> -					video->imagesize);
> +		ret = uvc_alloc_buffers(&video->queue, rb);
>  		if (ret < 0)
>  			return ret;
>  
> -		rb->count = ret;
>  		ret = 0;
>  		break;
>  	}
> @@ -231,9 +225,6 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
>  	{
>  		struct v4l2_buffer *buf = arg;
>  
> -		if (buf->type != video->queue.type)
> -			return -EINVAL;
> -
>  		return uvc_query_buffer(&video->queue, buf);
>  	}
>  
> @@ -251,7 +242,7 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
>  	{
>  		int *type = arg;
>  
> -		if (*type != video->queue.type)
> +		if (*type != video->queue.queue.type)
>  			return -EINVAL;
>  
>  		return uvc_video_enable(video, 1);
> @@ -261,14 +252,14 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
>  	{
>  		int *type = arg;
>  
> -		if (*type != video->queue.type)
> +		if (*type != video->queue.queue.type)
>  			return -EINVAL;
>  
>  		return uvc_video_enable(video, 0);
>  	}
>  
>  	/* Events */
> -        case VIDIOC_DQEVENT:
> +	case VIDIOC_DQEVENT:
>  	{
>  		struct v4l2_event *event = arg;
>  
> diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/uvc_video.c
> index f7d1913..2e06f24 100644
> --- a/drivers/usb/gadget/uvc_video.c
> +++ b/drivers/usb/gadget/uvc_video.c
> @@ -32,7 +32,7 @@ uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf,
>  	data[0] = 2;
>  	data[1] = UVC_STREAM_EOH | video->fid;
>  
> -	if (buf->buf.bytesused - video->queue.buf_used <= len - 2)
> +	if (buf->bytesused - video->queue.buf_used <= len - 2)
>  		data[1] |= UVC_STREAM_EOF;
>  
>  	return 2;
> @@ -47,8 +47,8 @@ uvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf,
>  	void *mem;
>  
>  	/* Copy video data to the USB buffer. */
> -	mem = queue->mem + buf->buf.m.offset + queue->buf_used;
> -	nbytes = min((unsigned int)len, buf->buf.bytesused - queue->buf_used);
> +	mem = buf->mem + queue->buf_used;
> +	nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used);
>  
>  	memcpy(data, mem, nbytes);
>  	queue->buf_used += nbytes;
> @@ -82,7 +82,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
>  	req->length = video->req_size - len;
>  	req->zero = video->payload_size == video->max_payload_size;
>  
> -	if (buf->buf.bytesused == video->queue.buf_used) {
> +	if (buf->bytesused == video->queue.buf_used) {
>  		video->queue.buf_used = 0;
>  		buf->state = UVC_BUF_STATE_DONE;
>  		uvc_queue_next_buffer(&video->queue, buf);
> @@ -92,7 +92,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
>  	}
>  
>  	if (video->payload_size == video->max_payload_size ||
> -	    buf->buf.bytesused == video->queue.buf_used)
> +	    buf->bytesused == video->queue.buf_used)
>  		video->payload_size = 0;
>  }
>  
> @@ -115,7 +115,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
>  
>  	req->length = video->req_size - len;
>  
> -	if (buf->buf.bytesused == video->queue.buf_used) {
> +	if (buf->bytesused == video->queue.buf_used) {
>  		video->queue.buf_used = 0;
>  		buf->state = UVC_BUF_STATE_DONE;
>  		uvc_queue_next_buffer(&video->queue, buf);
> @@ -161,6 +161,7 @@ static void
>  uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
>  {
>  	struct uvc_video *video = req->context;
> +	struct uvc_video_queue *queue = &video->queue;
>  	struct uvc_buffer *buf;
>  	unsigned long flags;
>  	int ret;
> @@ -169,13 +170,15 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
>  	case 0:
>  		break;
>  
> -	case -ESHUTDOWN:
> +	case -ESHUTDOWN:	/* disconnect from host. */
>  		printk(KERN_INFO "VS request cancelled.\n");
> +		uvc_queue_cancel(queue, 1);
>  		goto requeue;
>  
>  	default:
>  		printk(KERN_INFO "VS request completed with status %d.\n",
>  			req->status);
> +		uvc_queue_cancel(queue, 0);
>  		goto requeue;
>  	}
>  
> @@ -387,4 +390,3 @@ uvc_video_init(struct uvc_video *video)
>  	uvc_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT);
>  	return 0;
>  }
> -
> -- 
> 1.7.2.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux