summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2018-05-22 11:42:47 +0300
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2018-05-22 11:42:47 +0300
commit117cd47e08db4e31d2f966cb4b22987d3e5d6bb7 (patch)
tree4fb5396cd3c3d063aec59ef1e9e1066325f33a71
parentab92702a44f8521d5deef3e2ac9f35514bbe7c9a (diff)
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 <laurent.pinchart@ideasonboard.com>
-rw-r--r--uvc-gadget.c12
-rw-r--r--v4l2.c92
-rw-r--r--v4l2.h25
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.
*/
@@ -172,6 +173,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
* @buffer: Buffer to be queued