From ab0c87921e03742df93e94a800fe0fcf80e5047b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 22 May 2018 11:43:37 +0300 Subject: v4l2: Replace USERPTR support with DMABUF support V4L2_MEMORY_USERPTR is deprecated, replace it with V4L2_MEMORY_DMABUF support for buffer sharing. Two new functions are added, v4l2_export_buffers() to export previously allocated buffers as dmabuf objects, and v4l2_import_buffers() to import dmabuf objects as backing store for V4L2 buffers. Signed-off-by: Laurent Pinchart --- v4l2.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- v4l2.h | 54 +++++++++++++++++++++++++++++--- 2 files changed, 147 insertions(+), 18 deletions(-) diff --git a/v4l2.c b/v4l2.c index 4484acb..a0036fd 100644 --- a/v4l2.c +++ b/v4l2.c @@ -512,7 +512,7 @@ int v4l2_alloc_buffers(struct v4l2_device *dev, enum v4l2_memory memtype, if (dev->nbufs != 0) return -EBUSY; - if (memtype != V4L2_MEMORY_MMAP && memtype != V4L2_MEMORY_USERPTR) + if (memtype != V4L2_MEMORY_MMAP && memtype != V4L2_MEMORY_DMABUF) return -EINVAL; /* Request the buffers from the driver. */ @@ -550,8 +550,10 @@ int v4l2_alloc_buffers(struct v4l2_device *dev, enum v4l2_memory memtype, memset(dev->buffers, 0, sizeof *dev->buffers * nbufs); - for (i = 0; i < dev->nbufs; ++i) + for (i = 0; i < dev->nbufs; ++i) { dev->buffers[i].index = i; + dev->buffers[i].dmabuf = -1; + } ret = 0; @@ -571,13 +573,10 @@ int v4l2_free_buffers(struct v4l2_device *dev) if (dev->nbufs == 0) return 0; - if (dev->memtype == V4L2_MEMORY_MMAP) { - for (i = 0; i < dev->nbufs; ++i) { - struct v4l2_video_buffer *buffer = &dev->buffers[i]; - - if (buffer->mem == NULL) - continue; + for (i = 0; i < dev->nbufs; ++i) { + struct v4l2_video_buffer *buffer = &dev->buffers[i]; + if (buffer->mem) { ret = munmap(buffer->mem, buffer->size); if (ret < 0) { printf("%s: unable to unmap buffer %u (%d)\n", @@ -586,8 +585,14 @@ int v4l2_free_buffers(struct v4l2_device *dev) } buffer->mem = NULL; - buffer->size = 0; } + + if (buffer->dmabuf != -1) { + close(buffer->dmabuf); + buffer->dmabuf = -1; + } + + buffer->size = 0; } memset(&rb, 0, sizeof rb); @@ -609,6 +614,88 @@ int v4l2_free_buffers(struct v4l2_device *dev) return 0; } +int v4l2_export_buffers(struct v4l2_device *dev) +{ + unsigned int i; + int ret; + + if (dev->nbufs == 0) + return -EINVAL; + + if (dev->memtype != V4L2_MEMORY_MMAP) + return -EINVAL; + + for (i = 0; i < dev->nbufs; ++i) { + struct v4l2_exportbuffer expbuf = { + .type = dev->type, + .index = i, + }; + + ret = ioctl(dev->fd, VIDIOC_EXPBUF, &expbuf); + if (ret < 0) { + printf("Failed to export buffer %u.\n", i); + return ret; + } + + dev->buffers[i].dmabuf = expbuf.fd; + + printf("%s: buffer %u exported with fd %u.\n", + dev->name, i, dev->buffers[i].dmabuf); + } + + return 0; +} + +int v4l2_import_buffers(struct v4l2_device *dev, unsigned int nbufs, + const struct v4l2_video_buffer *buffers) +{ + unsigned int i; + int ret; + + if (dev->nbufs == 0 || dev->nbufs > nbufs) + return -EINVAL; + + if (dev->memtype != V4L2_MEMORY_DMABUF) + return -EINVAL; + + for (i = 0; i < dev->nbufs; ++i) { + const struct v4l2_video_buffer *buffer = &buffers[i]; + struct v4l2_buffer buf = { + .index = i, + .type = dev->type, + .memory = dev->memtype, + }; + int fd; + + ret = ioctl(dev->fd, VIDIOC_QUERYBUF, &buf); + if (ret < 0) { + printf("%s: unable to query buffer %u (%d).\n", + dev->name, i, errno); + return -errno; + } + + if (buffer->size < buf.length) { + printf("%s: buffer %u too small (%u bytes required, %u bytes available.\n", + dev->name, i, buf.length, buffer->size); + return -EINVAL; + } + + fd = dup(buffer->dmabuf); + if (fd < 0) { + printf("%s: failed to duplicate dmabuf fd %d.\n", + dev->name, buffer->dmabuf); + return ret; + } + + printf("%s: buffer %u valid.\n", dev->name, i); + + dev->buffers[i].dmabuf = fd; + dev->buffers[i].size = buffer->size; + } + + return 0; +} + int v4l2_mmap_buffers(struct v4l2_device *dev) { unsigned int i; @@ -688,10 +775,8 @@ int v4l2_queue_buffer(struct v4l2_device *dev, struct v4l2_video_buffer *buffer) buf.type = dev->type; buf.memory = dev->memtype; - if (dev->memtype == V4L2_MEMORY_USERPTR) - buf.m.userptr = (unsigned long)dev->buffers[buffer->index].mem; - - buf.length = dev->buffers[buffer->index].size; + if (dev->memtype == V4L2_MEMORY_DMABUF) + buf.m.fd = (unsigned long)dev->buffers[buffer->index].dmabuf; if (dev->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) buf.bytesused = buffer->bytesused; diff --git a/v4l2.h b/v4l2.h index 2dad4c1..f9da5aa 100644 --- a/v4l2.h +++ b/v4l2.h @@ -31,7 +31,7 @@ * @error: True if an error occured while capturing video data for the buffer * @allocated: True if memory for the buffer has been allocated * @mem: Video data memory - * @fd: Video data dmabuf handle + * @dmabuf: Video data dmabuf handle */ struct v4l2_video_buffer { @@ -42,7 +42,7 @@ struct v4l2_video_buffer bool error; bool allocated; void *mem; - int fd; + int dmabuf; }; struct v4l2_device @@ -148,7 +148,7 @@ int v4l2_set_crop(struct v4l2_device *dev, struct v4l2_rect *rect); * * When @memtype is set to V4L2_MEMORY_MMAP the buffers are allocated by the * driver. They can then be mapped to userspace by calling v4l2_mmap_buffers(). - * When @memtype is set to V4L2_MEMORY_USERPTR the driver only allocates buffer + * When @memtype is set to V4L2_MEMORY_DMABUF the driver only allocates buffer * objects and relies on the application to provide memory storage for video * frames. * @@ -162,7 +162,7 @@ int v4l2_alloc_buffers(struct v4l2_device *dev, enum v4l2_memory memtype, * @dev: Device instance * * Free buffers previously allocated by v4l2_alloc_buffers(). If the buffers - * have been allocated with the V4L2_MEMORY_USERPTR memory type only the buffer + * have been allocated with the V4L2_MEMORY_DMABUF memory type only the buffer * objects are freed, and the caller is responsible for freeing the video frames * memory if required. * @@ -172,6 +172,50 @@ int v4l2_alloc_buffers(struct v4l2_device *dev, enum v4l2_memory memtype, */ int v4l2_free_buffers(struct v4l2_device *dev); +/* + * v4l2_export_buffers - Export buffers as dmabuf objects + * @dev: Device instance + * + * Export all the buffers previously allocated by v4l2_alloc_buffers() as dmabuf + * objects. The dmabuf objects handles can be accessed through the dmabuf field + * of each entry in the @dev::buffers array. + * + * The dmabuf objects handles will be automatically closed when the buffers are + * freed with v4l2_free_buffers(). + * + * This function can only be called when buffers have been allocated with the + * memory type set to V4L2_MEMORY_MMAP. If the memory type is different, or if + * no buffers have been allocated, it will return -EINVAL. + * + * Return 0 on success or a negative error code on failure. + */ +int v4l2_export_buffers(struct v4l2_device *dev); + +/* + * v4l2_import_buffers - Import buffer backing store as dmabuf objects + * @dev: Device instance + * @nbufs: Number of buffers in the @buffers array + * @buffers: Buffers to be imported + * + * Import the dmabuf objects from @buffers as backing store for the device + * buffers previously allocated by v4l2_alloc_buffers(). + * + * The dmabuf file handles are duplicated and stored in the dmabuf field of the + * @dev::buffers array. The handles from the @buffers array can thus be closed + * independently without impacting usage of the imported dmabuf objects. The + * duplicated file handles will be automatically closed when the buffers are + * freed with v4l2_free_buffers(). + * + * This function can only be called when buffers have been allocated with the + * memory type set to V4L2_MEMORY_DMABUF. If the memory type is different, if no + * buffers have been allocated, or if the number of allocated buffers is larger + * than @nbufs, it will return -EINVAL. + * + * Return 0 on success or a negative error code on failure. + */ +int v4l2_import_buffers(struct v4l2_device *dev, unsigned int nbufs, + const struct v4l2_video_buffer *buffers); + /* * v4l2_mmap_buffers - Map buffers to application memory space * @dev: Device instance @@ -202,7 +246,7 @@ int v4l2_mmap_buffers(struct v4l2_device *dev); * buffer to be queued. The index is zero-based and must be lower than the * number of allocated buffers. * - * For V4L2_MEMORY_USERPTR buffers, the caller must initialize the @buffer::mem + * For V4L2_MEMORY_DMABUF buffers, the caller must initialize the @buffer::dmabuf * field with the address of the video frame memory, and the @buffer:length * field with its size in bytes. For optimal performances the address and length * should be identical between v4l2_queue_buffer() calls for a given buffer -- cgit v1.2.3