V4L2_MEMORY_MMAP 导出 fd 需要使用 vb2_ioctl_expbuf (只能使用于VB2_MEMORY_MMAP 方式)。
int buffer_export(int v4lfd, enum v4l2_buf_type bt, int index, int *dmafd)
{
struct v4l2_exportbuffer expbuf;
memset(&expbuf, 0, sizeof(expbuf));
expbuf.type = bt;
expbuf.index = index;
if (ioctl(v4lfd, VIDIOC_EXPBUF, &expbuf) == -1) {
perror("VIDIOC_EXPBUF");
return -1;
}
*dmafd = expbuf.fd;
return 0;
}
int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type,
unsigned int index, unsigned int plane, unsigned int flags)
{
struct vb2_buffer *vb = NULL;
struct vb2_plane *vb_plane;
int ret;
struct dma_buf *dbuf;
if (q->memory != VB2_MEMORY_MMAP) {
dprintk(1, "queue is not currently set up for mmap\n");
return -EINVAL;
}
if (!q->mem_ops->get_dmabuf) {
dprintk(1, "queue does not support DMA buffer exporting\n");
return -EINVAL;
}
if (flags & ~(O_CLOEXEC | O_ACCMODE)) {
dprintk(1, "queue does support only O_CLOEXEC and access mode flags\n");
return -EINVAL;
}
if (type != q->type) {
dprintk(1, "invalid buffer type\n");
return -EINVAL;
}
if (index >= q->num_buffers) {
dprintk(1, "buffer index out of range\n");
return -EINVAL;
}
vb = q->bufs[index];
if (plane >= vb->num_planes) {
dprintk(1, "buffer plane out of range\n");
return -EINVAL;
}
if (vb2_fileio_is_active(q)) {
dprintk(1, "expbuf: file io in progress\n");
return -EBUSY;
}
vb_plane = &vb->planes[plane];
dbuf = call_ptr_memop(vb, get_dmabuf, vb_plane->mem_priv,
flags & O_ACCMODE);
if (IS_ERR_OR_NULL(dbuf)) {
dprintk(1, "failed to export buffer %d, plane %d\n",
index, plane);
return -EINVAL;
}
ret = dma_buf_fd(dbuf, flags & ~O_ACCMODE);
if (ret < 0) {
dprintk(3, "buffer %d, plane %d failed to export (%d)\n",
index, plane, ret);
dma_buf_put(dbuf);
return ret;
}
dprintk(3, "buffer %d, plane %d exported as %d descriptor\n",
index, plane, ret);
*fd = ret;
return 0;
}
EXPORT_SYMBOL_GPL(vb2_core_expbuf);
这里以 https://elixir.bootlin.com/linux/v4.1/source/drivers/media/v4l2-core/videobuf2-dma-sg.c
的buffer 申请方式为例说明。
在reqbuf 后 使用 VIDIOC_EXPBUF 得到buffer fd ,使用的是 vb2_dma_sg_get_dmabuf。
static struct dma_buf *vb2_dma_sg_get_dmabuf(void *buf_priv, unsigned long flags)
{
struct vb2_dma_sg_buf *buf = buf_priv;
struct dma_buf *dbuf;
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
exp_info.ops = &vb2_dma_sg_dmabuf_ops;
exp_info.size = buf->size;
exp_info.flags = flags;
exp_info.priv = buf;
if (WARN_ON(!buf->dma_sgt))
return NULL;
dbuf = dma_buf_export(&exp_info);
if (IS_ERR(dbuf))
return NULL;
/* dmabuf keeps reference to vb2 buffer */
atomic_inc(&buf->refcount);
return dbuf;
}
将此 dmafd 传递给kernel ,则kernel对此dma_buf 的 attach 等操作皆使用vb2_dma_sg_dmabuf_ops 。
static struct dma_buf_ops vb2_dma_sg_dmabuf_ops = {
.attach = vb2_dma_sg_dmabuf_ops_attach,
.detach = vb2_dma_sg_dmabuf_ops_detach,
.map_dma_buf = vb2_dma_sg_dmabuf_ops_map,
.unmap_dma_buf = vb2_dma_sg_dmabuf_ops_unmap,
.kmap = vb2_dma_sg_dmabuf_ops_kmap,
.kmap_atomic = vb2_dma_sg_dmabuf_ops_kmap,
.vmap = vb2_dma_sg_dmabuf_ops_vmap,
.mmap = vb2_dma_sg_dmabuf_ops_mmap,
.release = vb2_dma_sg_dmabuf_ops_release,
};
对此方式的一些想法:
使用 V4L2_MEMORY_MMAP 方式 申请后得到的fd 这种方式获取fd 的方式可以送给display去显示,这样用的都是camera 申请的buffer ,做到buffer 共享。但是目前的camera 框架都是从display 的surface去申请buffer,在去使用dma buf fd 的方式去到camera 共享,这种方式目前在应用上应该比较少。