Re: [PATCH 5/7] v4l: videobuf2: add read() and write() emulator

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

 



On Friday, November 19, 2010 16:55:42 Marek Szyprowski wrote:
> Add a generic file io (read and write) emulator for videobuf2. It uses
> MMAP memory type buffers and generic vb2 calls: req_bufs, qbuf and
> dqbuf. Video date is being copied from mmap buffers to userspace with
> standard copy_to_user() function. To add support for file io the driver
> needs to provide an additional callback - read_setup or write_setup. It
> should provide the default number of buffers used by emulator and flags.
> 
> With these flags one can detemine the style of read() or write()
> emulation. By default 'streaming' style is used. With
> VB2_FILEIO_READ_ONCE flag one can select 'one shot' mode for read()
> emulator. With VB2_FILEIO_WRITE_IMMEDIATE flag one can select immediate
> conversion of write calls to qbuf for write() emulator, so the vb2 will
> not wait until each buffer is filled completely before queueing it to
> the driver.
> 
> Signed-off-by: Marek Szyprowski <m.szyprowski@xxxxxxxxxxx>
> Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx>
> CC: Pawel Osciak <pawel@xxxxxxxxxx>
> ---
>  drivers/media/video/videobuf2-core.c |  396 +++++++++++++++++++++++++++++++++-
>  include/media/videobuf2-core.h       |   31 +++
>  2 files changed, 426 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
> index 828803f..bc497e3 100644
> --- a/drivers/media/video/videobuf2-core.c
> +++ b/drivers/media/video/videobuf2-core.c

<snip>

> +/**
> + * __vb2_init_fileio() - initialize file io emulator
> + * @q:		videobuf2 queue
> + * @read:	mode selector (1 means read, 0 means write)
> + */
> +static int __vb2_init_fileio(struct vb2_queue *q, int read)
> +{
> +	struct vb2_fileio_data *fileio;
> +	int i, ret;
> +	unsigned int count=0, flags=0;
> +
> +	/*
> +	 * Sanity check
> +	 */
> +	if ((read && !q->ops->read_setup) || (!read && !q->ops->write_setup))
> +		BUG();
> +
> +	/*
> +	 * Check if device supports mapping buffers to kernel virtual space.
> +	 */
> +	if (!q->alloc_ctx[0]->mem_ops->vaddr)
> +		return -EBUSY;
> +
> +	/*
> +	 * Check if steaming api has not been already activated.

typo: steaming -> streaming :-)

> +	 */
> +	if (q->streaming || q->num_buffers > 0)
> +		return -EBUSY;
> +
> +	/*
> +	 * Basic checks done, lets try to set up file io emulator
> +	 */
> +	if (read)
> +		ret = call_qop(q, read_setup, q, &count, &flags);
> +	else
> +		ret = call_qop(q, write_setup, q, &count, &flags);
> +	if (ret)
> +		return ret;
> +
> +	dprintk(3, "setting up file io: mode %s, count %d, flags %08x\n",
> +		(read) ? "read" : "write", count, flags);
> +
> +	fileio = kzalloc(sizeof(struct vb2_fileio_data), GFP_KERNEL);
> +	if (fileio == NULL)
> +		return -ENOMEM;
> +
> +	fileio->flags = flags;
> +
> +	/*
> +	 * Request buffers and use MMAP type to force driver
> +	 * to allocate buffers by itself.
> +	 */
> +	fileio->req.count = count;
> +	fileio->req.memory = V4L2_MEMORY_MMAP;
> +	fileio->req.type = q->type;
> +	ret = vb2_reqbufs(q, &fileio->req);
> +	if (ret)
> +		goto err_kfree;
> +
> +	/*
> +	 * Check if plane_count is correct
> +	 * (multiplane buffers are not supported).
> +	 */
> +	if (q->bufs[0]->num_planes != 1) {
> +		fileio->req.count = 0;
> +		ret = -EBUSY;

I'm not sure about this error code. I think EINVAL is better although it's not
ideal either. A debug message would certainly help here.

> +		goto err_reqbufs;
> +	}
> +
> +	/*
> +	 * Get kernel address of each buffer.
> +	 */
> +	for (i = 0; i < q->num_buffers; i++) {
> +		fileio->bufs[i].vaddr = vb2_plane_vaddr(q->bufs[i], 0);
> +		if (fileio->bufs[i].vaddr == NULL)
> +			goto err_reqbufs;
> +		fileio->bufs[i].size = vb2_plane_size(q->bufs[i], 0);
> +	}
> +
> +	/*
> +	 * Read mode requires pre queuing of all buffers.
> +	 */
> +	if (read) {
> +		/*
> +		 * Queue all buffers.
> +		 */
> +		for (i = 0; i < q->num_buffers; i++) {
> +			struct v4l2_buffer *b = &fileio->b;
> +			memset(b, 0, sizeof(*b));
> +			b->type = q->type;
> +			b->memory = q->memory;
> +			b->index = i;
> +			ret = vb2_qbuf(q, b);
> +			if (ret)
> +				goto err_reqbufs;
> +			fileio->bufs[i].queued = 1;
> +		}
> +
> +		/*
> +		 * Start streaming.
> +		 */
> +		ret = vb2_streamon(q, q->type);
> +		if (ret)
> +			goto err_reqbufs;
> +	}
> +
> +	q->fileio = fileio;
> +
> +	return ret;
> +
> +err_reqbufs:
> +	vb2_reqbufs(q, &fileio->req);
> +
> +err_kfree:
> +	kfree(fileio);
> +	return ret;
> +}
> +
> +/**
> + * __vb2_cleanup_fileio() - free resourced used by file io emulator
> + * @q:		videobuf2 queue
> + */
> +static int __vb2_cleanup_fileio(struct vb2_queue *q)
> +{
> +	struct vb2_fileio_data *fileio = q->fileio;
> +
> +	if (fileio) {
> +		/*
> +		 * Hack fileio context to enable direct calls to vb2 ioctl
> +		 * interface.
> +		 */
> +		q->fileio = NULL;
> +
> +		vb2_streamoff(q, q->type);
> +		fileio->req.count = 0;
> +		vb2_reqbufs(q, &fileio->req);
> +		kfree(fileio);
> +		dprintk(3, "file io emulator closed\n");
> +	}
> +	return 0;
> +}
> +
> +/**
> + * __vb2_perform_fileio() - free resourced used by read() emulator
> + * @q:		videobuf2 queue
> + * @data:	pointed to target userspace buffer
> + * @count:	number of bytes to read or write
> + * @ppos:	file handle position tracking pointer
> + * @nonblock:	mode selector (1 means blocking calls, 0 means nonblocking)
> + * @read:	access mode selector (1 means read, 0 means write)
> + */
> +static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count,
> +		loff_t *ppos, int nonblock, int read)
> +{
> +	struct vb2_fileio_data *fileio;
> +	struct vb2_fileio_buf *buf;
> +	int ret, index;
> +
> +	dprintk(3, "file io: mode %s, offset %ld, count %d, %sblocking\n",
> +		read ? "read" : "write", (long)*ppos, count,
> +		nonblock ? "non" : "");
> +
> +	if (!data)
> +		return -EINVAL;
> +
> +	/*
> +	 * Initialize emulator on first call.
> +	 */
> +	if (!q->fileio) {
> +		ret = __vb2_init_fileio(q, read);
> +		dprintk(3, "file io: vb2_init_fileio result: %d\n", ret);
> +		if (ret)
> +			return ret;
> +	}
> +	fileio = q->fileio;
> +
> +	/*
> +	 * Hack fileio context to enable direct calls to vb2 ioctl interface.
> +	 * The pointer will be restored before returning from this function.
> +	 */
> +	q->fileio = NULL;
> +
> +	index = fileio->index;
> +	buf = &fileio->bufs[index];
> +
> +	/*
> +	 * Check if we need to dequeue the buffer.
> +	 */
> +	if (buf->queued) {
> +		struct vb2_buffer *vb;
> +
> +		/*
> +		 * Call vb2_dqbuf to get buffer back.
> +		 */
> +		memset(&fileio->b, 0, sizeof(fileio->b));
> +		fileio->b.type = q->type;
> +		fileio->b.memory = q->memory;
> +		fileio->b.index = index;
> +		ret = vb2_dqbuf(q, &fileio->b, nonblock);
> +		dprintk(5, "file io: vb2_dqbuf result: %d\n", ret);
> +		if (ret)
> +			goto end;
> +		fileio->dq_count += 1;
> +
> +		/*
> +		 * Get number of bytes filled by the driver
> +		 */
> +		vb = q->bufs[index];
> +		buf->size = vb2_get_plane_payload(vb, 0);
> +		buf->queued = 0;
> +	}
> +
> +	/*
> +	 * Limit count on last few bytes of the buffer.
> +	 */
> +	if (buf->pos + count > buf->size) {
> +		count = buf->size - buf->pos;
> +		dprintk(5, "reducing read count: %d\n", count);
> +	}
> +
> +	/*
> +	 * Transfer data to userspace.
> +	 */
> +	dprintk(3, "file io: copying %d bytes - buffer %d, offset %d\n",
> +		count, index, buf->pos);
> +	if (read)
> +		ret = copy_to_user(data, buf->vaddr + buf->pos, count);
> +	else
> +		ret = copy_from_user(buf->vaddr + buf->pos, data, count);
> +	if (ret) {
> +		dprintk(3, "file io: error copying data\n");
> +		ret = -EFAULT;
> +		goto end;
> +	}
> +
> +	/*
> +	 * Update counters.
> +	 */
> +	buf->pos += count;
> +	*ppos += count;
> +
> +	/*
> +	 * Queue next buffer if required.
> +	 */
> +	if (buf->pos == buf->size ||
> +	   (!read && (fileio->flags & VB2_FILEIO_WRITE_IMMEDIATE))) {
> +		/*
> +		 * Check if this is the last buffer to read.
> +		 */
> +		if (read && (fileio->flags & VB2_FILEIO_READ_ONCE) &&
> +		    fileio->dq_count == 1) {
> +			dprintk(3, "file io: read limit reached\n");
> +			/*
> +			 * Restore fileio pointer and release the context.
> +			 */
> +			q->fileio = fileio;
> +			return __vb2_cleanup_fileio(q);
> +		}
> +
> +		/*
> +		 * Call vb2_qbuf and give buffer to the driver.
> +		 */
> +		memset(&fileio->b, 0, sizeof(fileio->b));
> +		fileio->b.type = q->type;
> +		fileio->b.memory = q->memory;
> +		fileio->b.index = index;
> +		fileio->b.bytesused = buf->pos;
> +		ret = vb2_qbuf(q, &fileio->b);
> +		dprintk(5, "file io: vb2_dbuf result: %d\n", ret);
> +		if (ret)
> +			goto end;
> +
> +		/*
> +		 * Buffer has been queued, update the status
> +		 */
> +		buf->pos = 0;
> +		buf->queued = 1;
> +		buf->size = q->bufs[0]->v4l2_planes[0].length;
> +		fileio->q_count += 1;
> +
> +		/*
> +		 * Switch to the next buffer
> +		 */
> +		fileio->index = (index + 1) % q->num_buffers;
> +
> +		/*
> +		 * Start streaming if required.
> +		 */
> +		if (!read && !q->streaming) {
> +			ret = vb2_streamon(q, q->type);
> +			if (ret)
> +				goto end;
> +		}
> +	}
> +
> +	/*
> +	 * Return proper number of bytes processed.
> +	 */
> +	if (ret == 0)
> +		ret = count;
> +end:
> +	/*
> +	 * Restore the fileio context and block vb2 ioctl interface.
> +	 */
> +	q->fileio = fileio;
> +	return ret;
> +}
> +
> +size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
> +		loff_t *ppos, int nonblocking)
> +{
> +	return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1);
> +}
> +EXPORT_SYMBOL_GPL(vb2_read);
> +
> +size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count,
> +		loff_t *ppos, int nonblocking)
> +{
> +	return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 0);
> +}
> +EXPORT_SYMBOL_GPL(vb2_write);
> +
>  MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2");
> -MODULE_AUTHOR("Pawel Osciak");
> +MODULE_AUTHOR("Pawel Osciak, Marek Szyprowski");
>  MODULE_LICENSE("GPL");
> diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
> index 5235a0d..66a25b8 100644
> --- a/include/media/videobuf2-core.h
> +++ b/include/media/videobuf2-core.h
> @@ -18,6 +18,7 @@
>  #include <linux/poll.h>
>  
>  struct vb2_alloc_ctx;
> +struct vb2_fileio_data;
>  
>  /**
>   * struct vb2_mem_ops - memory handling/memory allocator operations
> @@ -54,6 +55,7 @@ struct vb2_alloc_ctx;
>   *
>   * Required ops for USERPTR types: get_userptr, put_userptr.
>   * Required ops for MMAP types: alloc, put, num_users, mmap.
> + * Required ops for read/write access types: alloc, put, num_users, vaddr
>   */
>  struct vb2_mem_ops {
>  	void		*(*alloc)(const struct vb2_alloc_ctx *alloc_ctx,
> @@ -96,6 +98,17 @@ struct vb2_plane {
>  };
>  
>  /**
> + * enum vb2_fileio_flags - flags for selecting a mode of the file io emulator,
> + * by default the 'streaming' style is used by the file io emulator
> + * @VB2_FILEIO_READ_ONCE:	report EOF after reading the first buffer
> + * @VB2_FILEIO_WRITE_IMMEDIATE:	queue buffer after each write() call
> + */
> +enum vb2_fileio_flags {
> +	VB2_FILEIO_READ_ONCE		= (1<<0),
> +	VB2_FILEIO_WRITE_IMMEDIATE	= (1<<1),
> +};
> +
> +/**
>   * enum vb2_buffer_state - current video buffer state
>   * @VB2_BUF_STATE_DEQUEUED:	buffer under userspace control
>   * @VB2_BUF_STATE_QUEUED:	buffer queued in videobuf, but not in driver
> @@ -170,6 +183,12 @@ struct vb2_buffer {
>   * @plane_setup:	called before memory allocation num_planes times;
>   *			driver should return the required size of plane number
>   *			plane_no
> + * @read_setup:		called before enabling read() style io; asks the driver
> + *			for the number of buffers and flags used by the
> + *			emulator; see vb2_fileio_flags for more information
> + * @write_setup:	called before enabling write() style io; asks the driver
> + *			for the number of buffers and flags used by the
> + *			emulator; see vb2_fileio_flags for more information
>   * @unlock:		release any locks taken while calling vb2 functions;
>   *			it is called before poll_wait function in vb2_poll
>   *			implementation; required to avoid deadlock when vb2_poll
> @@ -215,6 +234,11 @@ struct vb2_ops {
>  	int (*plane_setup)(struct vb2_queue *q,
>  			   unsigned int plane_no, unsigned long *plane_size);
>  
> +	int (*read_setup)(struct vb2_queue *q,
> +			  unsigned int *count, unsigned int *flags);
> +	int (*write_setup)(struct vb2_queue *q,
> +			   unsigned int *count, unsigned int *flags);
> +
>  	void (*unlock)(struct vb2_queue *q);
>  	void (*lock)(struct vb2_queue *q);
>  
> @@ -249,6 +273,7 @@ struct vb2_ops {
>   * @streaming:	current streaming state
>   * @userptr_supported: true if queue supports USERPTR types
>   * @mmap_supported: true if queue supports MMAP types
> + * @fileio:	file io emulator internal data, used only if emulator is active
>   */
>  struct vb2_queue {
>  	enum v4l2_buf_type		type;
> @@ -271,6 +296,8 @@ struct vb2_queue {
>  	int				streaming:1;
>  	int				userptr_supported:1;
>  	int				mmap_supported:1;
> +
> +	struct vb2_fileio_data		*fileio;
>  };
>  
>  void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no);
> @@ -295,6 +322,10 @@ int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type);
>  
>  int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma);
>  unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait);
> +size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
> +		loff_t *ppos, int nonblock);
> +size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count,
> +		loff_t *ppos, int nonblock);
>  
>  /**
>   * vb2_is_streaming() - return streaming status of the queue
> 

Very nice code! Clean and to the point.

Thank you once again for all your work on this!

Regards,

	Hans

-- 
Hans Verkuil - video4linux developer - sponsored by Cisco
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[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