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