This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit dea9f715da6042e3039609ec5d195c06cabfa48f
Author: yaojingwei <[email protected]>
AuthorDate: Fri Jan 16 09:50:08 2026 +0800

    v4l2_cap: support dma buffer export
    
    Add VIDIOC_EXPBUF and struct v4l2_exportbuffer. With
    CONFIG_DMA_SHARED_BUFFER enabled, the capture driver exports an MMAP
    capture buffer as a dma-buf file descriptor, so another driver or
    process can import the frame without copying it. The exported buffer
    supports attach/map, CPU access with cache maintenance, and mmap.
    
    The fd returned to the caller holds its own reference, so the driver
    drops its reference after dma_buf_fd().
    
    Signed-off-by: yaojingwei <[email protected]>
---
 drivers/video/v4l2_cap.c    | 219 +++++++++++++++++++++++++++++++++++++++++++-
 drivers/video/v4l2_core.c   |  11 +++
 include/nuttx/video/video.h |   4 +
 include/sys/videoio.h       |  12 +++
 4 files changed, 243 insertions(+), 3 deletions(-)

diff --git a/drivers/video/v4l2_cap.c b/drivers/video/v4l2_cap.c
index f86c27b1afe..fc46f4293e0 100644
--- a/drivers/video/v4l2_cap.c
+++ b/drivers/video/v4l2_cap.c
@@ -37,6 +37,10 @@
 #include <nuttx/spinlock.h>
 #include <nuttx/video/v4l2_cap.h>
 #include <nuttx/video/video.h>
+#ifdef CONFIG_DMA_SHARED_BUFFER
+#  include <nuttx/dma-buf.h>
+#  include <nuttx/cache.h>
+#endif
 
 #include "video_framebuff.h"
 
@@ -292,6 +296,10 @@ static int capture_enum_frminterval(FAR struct file *filep,
                                     FAR struct v4l2_frmivalenum *f);
 static int capture_enum_frmsize(FAR struct file *filep,
                                 FAR struct v4l2_frmsizeenum *f);
+#ifdef CONFIG_DMA_SHARED_BUFFER
+static int capture_expbuf(FAR struct file *filep,
+                          FAR struct v4l2_exportbuffer *ebuf);
+#endif
 
 /* File operations function */
 
@@ -304,11 +312,44 @@ static int capture_poll(FAR struct file *filep,
 #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
 static int capture_unlink(FAR struct inode *inode);
 #endif
+#ifdef CONFIG_DMA_SHARED_BUFFER
+static int capture_dma_attach(FAR struct dma_buf *dmabuf,
+                              FAR struct dma_buf_attachment *attachment);
+static void capture_dma_detach(FAR struct dma_buf *dmabuf,
+                               FAR struct dma_buf_attachment *attachment);
+static uintptr_t
+capture_dma_map_buf(FAR struct dma_buf_attachment *attachment,
+                    enum dma_data_direction direction);
+static void capture_dma_unmap_buf(FAR struct dma_buf_attachment *attachment,
+                                  uintptr_t buf,
+                                  enum dma_data_direction direction);
+static void capture_dma_release(FAR struct dma_buf *dmabuf);
+static int capture_dma_begin_cpu_access(FAR struct dma_buf *dmabuf,
+                                        enum dma_data_direction direction);
+static int capture_dma_end_cpu_access(FAR struct dma_buf *dmabuf,
+                                      enum dma_data_direction direction);
+static int capture_dma_mmap(FAR struct dma_buf *dmabuf,
+                            FAR struct mm_map_entry_s *vma);
+#endif
 
 /****************************************************************************
  * Private Data
  ****************************************************************************/
 
+#ifdef CONFIG_DMA_SHARED_BUFFER
+static struct dma_buf_ops g_capture_dma_buf_ops =
+{
+  .attach = capture_dma_attach,
+  .detach = capture_dma_detach,
+  .map_dma_buf = capture_dma_map_buf,
+  .unmap_dma_buf = capture_dma_unmap_buf,
+  .release = capture_dma_release,
+  .begin_cpu_access = capture_dma_begin_cpu_access,
+  .end_cpu_access = capture_dma_end_cpu_access,
+  .mmap = capture_dma_mmap,
+};
+#endif
+
 static const struct v4l2_ops_s g_capture_vops =
 {
   capture_querycap,                   /* querycap */
@@ -344,7 +385,15 @@ static const struct v4l2_ops_s g_capture_vops =
   capture_s_ext_ctrls_scene,          /* s_ext_ctrls_scene */
   capture_enum_fmt,                   /* enum_fmt */
   capture_enum_frminterval,           /* enum_frminterval */
-  capture_enum_frmsize                /* enum_frmsize */
+  capture_enum_frmsize,               /* enum_frmsize */
+  NULL,                               /* cropcap */
+  NULL,                               /* dqevent */
+  NULL,                               /* subscribe_event */
+  NULL,                               /* decoder_cmd */
+  NULL,                               /* encoder_cmd */
+#ifdef CONFIG_DMA_SHARED_BUFFER
+  capture_expbuf,                     /* expbuf */
+#endif
 };
 
 static const struct file_operations g_capture_fops =
@@ -548,6 +597,94 @@ static FAR struct imgdata_s *g_capture_data = NULL;
  * Private Functions
  ****************************************************************************/
 
+#ifdef CONFIG_DMA_SHARED_BUFFER
+static int capture_dma_attach(FAR struct dma_buf *dmabuf,
+                              FAR struct dma_buf_attachment *attachment)
+{
+  attachment->priv = dmabuf->priv;
+  return 0;
+}
+
+static void capture_dma_detach(FAR struct dma_buf *dmabuf,
+                               FAR struct dma_buf_attachment *attachment)
+{
+}
+
+static uintptr_t
+capture_dma_map_buf(FAR struct dma_buf_attachment *attachment,
+                    enum dma_data_direction direction)
+{
+  FAR v4l2_buffer_t *buf = (FAR v4l2_buffer_t *)attachment->priv;
+
+  return (uintptr_t)buf->m.vaddr;
+}
+
+static void capture_dma_unmap_buf(FAR struct dma_buf_attachment *attachment,
+                                  uintptr_t buf,
+                                  enum dma_data_direction direction)
+{
+}
+
+static void capture_dma_release(FAR struct dma_buf *dmabuf)
+{
+  FAR v4l2_buffer_t *buf = (FAR v4l2_buffer_t *)dmabuf->priv;
+
+  if (buf)
+    {
+      kmm_free(buf);
+      dmabuf->priv = NULL;
+    }
+}
+
+static int capture_dma_begin_cpu_access(FAR struct dma_buf *dmabuf,
+                                        enum dma_data_direction direction)
+{
+  FAR v4l2_buffer_t *buf = (FAR v4l2_buffer_t *)dmabuf->priv;
+
+  if (buf == NULL)
+    {
+      return -EINVAL;
+    }
+
+  if (direction == DMA_FROM_DEVICE || direction == DMA_BIDIRECTIONAL)
+    {
+      up_invalidate_dcache((uintptr_t)buf->m.vaddr,
+                           (uintptr_t)(buf->m.vaddr + buf->length));
+    }
+
+  return 0;
+}
+
+static int capture_dma_end_cpu_access(FAR struct dma_buf *dmabuf,
+                                      enum dma_data_direction direction)
+{
+  FAR v4l2_buffer_t *buf = (FAR v4l2_buffer_t *)dmabuf->priv;
+
+  if (buf == NULL)
+    {
+      return -EINVAL;
+    }
+
+  if (direction == DMA_TO_DEVICE || direction == DMA_BIDIRECTIONAL)
+    {
+      up_clean_dcache((uintptr_t)buf->m.vaddr,
+                      (uintptr_t)(buf->m.vaddr + buf->length));
+    }
+
+  return 0;
+}
+
+static int capture_dma_mmap(FAR struct dma_buf *dmabuf,
+                            FAR struct mm_map_entry_s *vma)
+{
+  FAR v4l2_buffer_t *buf = (FAR v4l2_buffer_t *)dmabuf->priv;
+
+  vma->length = buf->length;
+  vma->vaddr = buf->m.vaddr;
+  return 0;
+}
+#endif
+
 static FAR capture_type_inf_t *
 get_capture_type_inf(FAR capture_mng_t *cmng, uint8_t type)
 {
@@ -2284,7 +2421,14 @@ static int capture_querybuf(FAR struct file *filep,
   FAR capture_mng_t *cmng = inode->i_private;
   FAR capture_type_inf_t *type_inf;
 
-  if (cmng == NULL || buf == NULL || buf->memory != V4L2_MEMORY_MMAP)
+#ifdef CONFIG_DMA_SHARED_BUFFER
+  if (cmng == NULL || buf == NULL ||
+      (buf->memory != V4L2_MEMORY_MMAP &&
+       buf->memory != V4L2_MEMORY_DMABUF))
+#else
+  if (cmng == NULL || buf == NULL ||
+      buf->memory != V4L2_MEMORY_MMAP)
+#endif
     {
       return -EINVAL;
     }
@@ -2301,7 +2445,10 @@ static int capture_querybuf(FAR struct file *filep,
     }
 
   buf->length = get_bufsize(&type_inf->fmt[CAPTURE_FMT_MAIN]);
-  buf->m.offset = buf->length * buf->index;
+  if (buf->memory == V4L2_MEMORY_MMAP)
+    {
+      buf->m.offset = buf->length * buf->index;
+    }
 
   return OK;
 }
@@ -2338,7 +2485,11 @@ static int capture_qbuf(FAR struct file *filep,
     }
 
   memcpy(&container->buf, buf, sizeof(struct v4l2_buffer));
+#ifdef CONFIG_DMA_SHARED_BUFFER
+  if (buf->memory == V4L2_MEMORY_MMAP || buf->memory == V4L2_MEMORY_DMABUF)
+#else
   if (buf->memory == V4L2_MEMORY_MMAP)
+#endif
     {
       /* only use userptr inside the container */
 
@@ -3608,6 +3759,68 @@ static int capture_enum_frminterval(FAR struct file 
*filep,
   return 0;
 }
 
+#ifdef CONFIG_DMA_SHARED_BUFFER
+static int capture_expbuf(FAR struct file *filep,
+                          FAR struct v4l2_exportbuffer *ebuf)
+{
+  FAR struct inode *inode = filep->f_inode;
+  FAR capture_mng_t *cmng = inode->i_private;
+  FAR capture_type_inf_t *type_inf;
+  struct dma_buf_export_info exp_info;
+  FAR v4l2_buffer_t *buf;
+  struct dma_buf *dbuf;
+  int ret;
+
+  type_inf = get_capture_type_inf(cmng, ebuf->type);
+  if (type_inf == NULL)
+    {
+      return -EINVAL;
+    }
+
+  if (ebuf->index >= type_inf->bufinf.container_size)
+    {
+      return -EINVAL;
+    }
+
+  buf = kmm_zalloc(sizeof(v4l2_buffer_t));
+  if (buf == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  buf->index = ebuf->index;
+  buf->length = get_bufsize(&type_inf->fmt[CAPTURE_FMT_MAIN]);
+  buf->m.vaddr = type_inf->bufheap + ebuf->index * buf->length;
+
+  memset(&exp_info, 0, sizeof(exp_info));
+  exp_info.priv = buf;
+  exp_info.size = buf->length;
+  exp_info.ops = &g_capture_dma_buf_ops;
+  exp_info.flags = ebuf->flags;
+  dbuf = dma_buf_export(&exp_info);
+  if (!dbuf)
+    {
+      verr("dma_buf_export failed\n");
+      kmm_free(buf);
+      return -EINVAL;
+    }
+
+  ret = dma_buf_fd(dbuf, ebuf->flags);
+  dma_buf_put(dbuf);
+  if (ret < 0)
+    {
+      verr("dma_buf_fd failed\n");
+      return ret;
+    }
+
+  ebuf->fd = ret;
+
+  vinfo("dma_buf_export fd: %d\n", ebuf->fd);
+
+  return 0;
+}
+#endif
+
 /****************************************************************************
  * File Opterations Functions
  ****************************************************************************/
diff --git a/drivers/video/v4l2_core.c b/drivers/video/v4l2_core.c
index a1396d6f387..7de35e0a866 100644
--- a/drivers/video/v4l2_core.c
+++ b/drivers/video/v4l2_core.c
@@ -494,6 +494,17 @@ static int v4l2_ioctl(FAR struct file *filep,
         return v4l2->vops->encoder_cmd(filep,
                              (FAR struct v4l2_encoder_cmd *)arg);
 
+#ifdef CONFIG_DMA_SHARED_BUFFER
+      case VIDIOC_EXPBUF:
+        if (v4l2->vops->expbuf == NULL)
+          {
+            break;
+          }
+
+        return v4l2->vops->expbuf(filep,
+                             (FAR struct v4l2_exportbuffer *)arg);
+#endif
+
       default:
         verr("Unrecognized cmd: %d\n", cmd);
         break;
diff --git a/include/nuttx/video/video.h b/include/nuttx/video/video.h
index d0c7d4d28f7..5539d215c7e 100644
--- a/include/nuttx/video/video.h
+++ b/include/nuttx/video/video.h
@@ -147,6 +147,10 @@ struct v4l2_ops_s
                           FAR struct v4l2_decoder_cmd *cmd);
   CODE int (*encoder_cmd)(FAR struct file *filep,
                           FAR struct v4l2_encoder_cmd *cmd);
+#ifdef CONFIG_DMA_SHARED_BUFFER
+  CODE int (*expbuf)(FAR struct file *filep,
+                     FAR struct v4l2_exportbuffer *ebuf);
+#endif
 };
 
 /****************************************************************************
diff --git a/include/sys/videoio.h b/include/sys/videoio.h
index d2c2e125ae9..833a6a524b0 100644
--- a/include/sys/videoio.h
+++ b/include/sys/videoio.h
@@ -1499,6 +1499,18 @@ struct v4s_querymenu_scene
   struct v4l2_querymenu      menu;     /* Same as VIDIOC_QUERYMENU */
 };
 
+/* Structure for VIDIOC_EXPBUF */
+
+struct v4l2_exportbuffer
+{
+  uint32_t type; /* enum v4l2_buf_type */
+  uint32_t index;
+  uint32_t plane;
+  uint32_t flags;
+  uint32_t fd;
+  uint32_t reserved[11];
+};
+
 #define V4L2_ENC_CMD_START      (0)
 #define V4L2_ENC_CMD_STOP       (1)
 #define V4L2_ENC_CMD_PAUSE      (2)

Reply via email to