v4l2: Split buffer allocation and mapping
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Tue, 22 May 2018 08:42:47 +0000 (11:42 +0300)
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Tue, 22 May 2018 08:42:47 +0000 (11:42 +0300)
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>
uvc-gadget.c
v4l2.c
v4l2.h

index a0d8004..df9d80a 100644 (file)
@@ -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 (file)
--- 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 (file)
--- 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