From 117cd47e08db4e31d2f966cb4b22987d3e5d6bb7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 22 May 2018 11:42:47 +0300 Subject: v4l2: Split buffer allocation and mapping Not all use cases of V4L2_MEMORY_MMAP require mapping buffers to userspace. Separate the allocation and mapping to allow usage of unmapped buffers. Signed-off-by: Laurent Pinchart --- uvc-gadget.c | 12 +++++++- v4l2.c | 92 +++++++++++++++++++++++++++++------------------------------- v4l2.h | 25 +++++++++++++++-- 3 files changed, 78 insertions(+), 51 deletions(-) diff --git a/uvc-gadget.c b/uvc-gadget.c index a0d8004..df9d80a 100644 --- a/uvc-gadget.c +++ b/uvc-gadget.c @@ -163,6 +163,12 @@ uvc_video_stream(struct uvc_device *dev, int enable) return ret; } + ret = v4l2_mmap_buffers(dev->vdev); + if (ret < 0) { + printf("Failed to mmap buffers.\n"); + goto error; + } + for (i = 0; i < dev->vdev->nbufs; ++i) { struct v4l2_video_buffer *buf = &dev->vdev->buffers[i]; @@ -170,13 +176,17 @@ uvc_video_stream(struct uvc_device *dev, int enable) ret = v4l2_queue_buffer(dev->vdev, buf); if (ret < 0) - return ret; + goto error; } v4l2_stream_on(dev->vdev); events_watch_fd(&dev->events, dev->vdev->fd, EVENT_WRITE, uvc_video_process, dev); + return 0; + +error: + v4l2_free_buffers(dev->vdev); return ret; } diff --git a/v4l2.c b/v4l2.c index 8d5e847..4484acb 100644 --- a/v4l2.c +++ b/v4l2.c @@ -506,13 +506,15 @@ int v4l2_alloc_buffers(struct v4l2_device *dev, enum v4l2_memory memtype, unsigned int nbufs) { struct v4l2_requestbuffers rb; - struct v4l2_buffer buf; unsigned int i; int ret; if (dev->nbufs != 0) return -EBUSY; + if (memtype != V4L2_MEMORY_MMAP && memtype != V4L2_MEMORY_USERPTR) + return -EINVAL; + /* Request the buffers from the driver. */ memset(&rb, 0, sizeof rb); rb.count = nbufs; @@ -551,52 +553,6 @@ int v4l2_alloc_buffers(struct v4l2_device *dev, enum v4l2_memory memtype, for (i = 0; i < dev->nbufs; ++i) dev->buffers[i].index = i; - /* Map the buffers. */ - for (i = 0; i < rb.count; ++i) { - memset(&buf, 0, sizeof buf); - buf.index = i; - buf.type = dev->type; - buf.memory = memtype; - ret = ioctl(dev->fd, VIDIOC_QUERYBUF, &buf); - if (ret < 0) { - printf("%s: unable to query buffer %u (%d).\n", - dev->name, i, errno); - ret = -errno; - goto done; - } - - switch (memtype) { - case V4L2_MEMORY_MMAP: - dev->buffers[i].mem = mmap(0, buf.length, PROT_READ | PROT_WRITE, - MAP_SHARED, dev->fd, buf.m.offset); - if (dev->buffers[i].mem == MAP_FAILED) { - printf("%s: unable to map buffer %u (%d)\n", - dev->name, i, errno); - ret = -errno; - goto done; - } - dev->buffers[i].size = buf.length; - printf("%s: buffer %u mapped at address %p.\n", - dev->name, i, dev->buffers[i].mem); - break; - - case V4L2_MEMORY_USERPTR: - if (dev->buffers[i].size < buf.length) { - printf("%s: buffer %u too small (%u bytes required, " - "%u bytes available.\n", dev->name, i, - buf.length, dev->buffers[i].size); - ret = -EINVAL; - goto done; - } - - printf("%s: buffer %u valid.\n", dev->name, i); - break; - - default: - break; - } - } - ret = 0; done: @@ -653,6 +609,48 @@ int v4l2_free_buffers(struct v4l2_device *dev) return 0; } +int v4l2_mmap_buffers(struct v4l2_device *dev) +{ + unsigned int i; + int ret; + + if (dev->memtype != V4L2_MEMORY_MMAP) + return -EINVAL; + + for (i = 0; i < dev->nbufs; ++i) { + struct v4l2_video_buffer *buffer = &dev->buffers[i]; + struct v4l2_buffer buf = { + .index = i, + .type = dev->type, + .memory = dev->memtype, + }; + void *mem; + + 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; + } + + mem = mmap(0, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, + dev->fd, buf.m.offset); + if (mem == MAP_FAILED) { + printf("%s: unable to map buffer %u (%d)\n", + dev->name, i, errno); + return -errno; + } + + buffer->mem = mem; + buffer->size = buf.length; + + printf("%s: buffer %u mapped at address %p.\n", dev->name, i, + mem); + } + + return 0; +} + int v4l2_dequeue_buffer(struct v4l2_device *dev, struct v4l2_video_buffer *buffer) { struct v4l2_buffer buf; diff --git a/v4l2.h b/v4l2.h index 4a22ecc..2dad4c1 100644 --- a/v4l2.h +++ b/v4l2.h @@ -147,9 +147,10 @@ int v4l2_set_crop(struct v4l2_device *dev, struct v4l2_rect *rect); * will be stored in the @dev::nbufs field. * * When @memtype is set to V4L2_MEMORY_MMAP the buffers are allocated by the - * driver and mapped to userspace. When @memtype is set to V4L2_MEMORY_USERPTR - * the driver only allocates buffer objects and relies on the application to - * provide memory storage for video frames. + * 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 + * objects and relies on the application to provide memory storage for video + * frames. * * Return 0 on success or a negative error code on failure. */ @@ -171,6 +172,24 @@ int v4l2_alloc_buffers(struct v4l2_device *dev, enum v4l2_memory memtype, */ int v4l2_free_buffers(struct v4l2_device *dev); +/* + * v4l2_mmap_buffers - Map buffers to application memory space + * @dev: Device instance + * + * Map all the buffers previously allocated by v4l2_alloc_buffers() to the + * application memory space. The buffer memory can be accessed through the mem + * field of each entry in the @dev::buffers array. + * + * Buffers will be automatically unmapped when 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_mmap_buffers(struct v4l2_device *dev); + /* * v4l2_queue_buffer - Queue a buffer for video capture/output * @dev: Device instance -- cgit v1.2.3