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.szyprow...@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.p...@samsung.com>
> CC: Pawel Osciak <pa...@osciak.com>
> ---
>  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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to