user space
参考 https://www.kernel.org/doc/html/v4.9/media/uapi/v4l/dmabuf.html
struct v4l2_buffer buf;
memset(&buf, 0, sizeof buf);
buf.index = 0;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_DMABUF;
buf.m.fd = dma_buf_fd;
ret = ioctl(v4lfd, VIDIOC_QBUF, &buf);
kernel
/**
* __qbuf_dmabuf() - handle qbuf of a DMABUF buffer
*/
static int __qbuf_dmabuf(struct vb2_buffer *vb, const void *pb)
{
struct vb2_plane planes[VB2_MAX_PLANES];
struct vb2_queue *q = vb->vb2_queue;
void *mem_priv;
unsigned int plane;
int ret = 0;
enum dma_data_direction dma_dir =
q->is_output ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
bool reacquired = vb->planes[0].mem_priv == NULL;
memset(planes, 0, sizeof(planes[0]) * vb->num_planes);
/* Copy relevant information provided by the userspace */
if (pb)
ret = call_bufop(vb->vb2_queue, fill_vb2_buffer,
vb, pb, planes);
if (ret)
return ret;
for (plane = 0; plane < vb->num_planes; ++plane) {
struct dma_buf *dbuf = dma_buf_get(planes[plane].m.fd);
if (IS_ERR_OR_NULL(dbuf)) {
dprintk(1, "invalid dmabuf fd for plane %d\n",
plane);
ret = -EINVAL;
goto err;
}
/* use DMABUF size if length is not provided */
if (planes[plane].length == 0)
planes[plane].length = dbuf->size;
if (planes[plane].length < vb->planes[plane].min_length) {
dprintk(1, "invalid dmabuf length %u for plane %d, minimum length %u\n",
planes[plane].length, plane,
vb->planes[plane].min_length);
dma_buf_put(dbuf);
ret = -EINVAL;
goto err;
}
/* Skip the plane if already verified */
if (dbuf == vb->planes[plane].dbuf &&
vb->planes[plane].length == planes[plane].length) {
dma_buf_put(dbuf);
continue;
}
dprintk(1, "buffer for plane %d changed\n", plane);
if (!reacquired) {
reacquired = true;
call_void_vb_qop(vb, buf_cleanup, vb);
}
/* Release previously acquired memory if present */
__vb2_plane_dmabuf_put(vb, &vb->planes[plane]);
vb->planes[plane].bytesused = 0;
vb->planes[plane].length = 0;
vb->planes[plane].m.fd = 0;
vb->planes[plane].data_offset = 0;
/* Acquire each plane's memory */
mem_priv = call_ptr_memop(vb, attach_dmabuf,
q->alloc_devs[plane] ? : q->dev,
dbuf, planes[plane].length, dma_dir);
if (IS_ERR(mem_priv)) {
dprintk(1, "failed to attach dmabuf\n");
ret = PTR_ERR(mem_priv);
dma_buf_put(dbuf);
goto err;
}
vb->planes[plane].dbuf = dbuf;
vb->planes[plane].mem_priv = mem_priv;
}
/*
* This pins the buffer(s) with dma_buf_map_attachment()). It's done
* here instead just before the DMA, while queueing the buffer(s) so
* userspace knows sooner rather than later if the dma-buf map fails.
*/
for (plane = 0; plane < vb->num_planes; ++plane) {
ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv);
if (ret) {
dprintk(1, "failed to map dmabuf for plane %d\n",
plane);
goto err;
}
vb->planes[plane].dbuf_mapped = 1;
}
/*
* Now that everything is in order, copy relevant information
* provided by userspace.
*/
for (plane = 0; plane < vb->num_planes; ++plane) {
vb->planes[plane].bytesused = planes[plane].bytesused;
vb->planes[plane].length = planes[plane].length;
vb->planes[plane].m.fd = planes[plane].m.fd;
vb->planes[plane].data_offset = planes[plane].data_offset;
}
if (reacquired) {
/*
* Call driver-specific initialization on the newly acquired buffer,
* if provided.
*/
ret = call_vb_qop(vb, buf_init, vb);
if (ret) {
dprintk(1, "buffer initialization failed\n");
goto err;
}
}
ret = call_vb_qop(vb, buf_prepare, vb);
if (ret) {
dprintk(1, "buffer preparation failed\n");
call_void_vb_qop(vb, buf_cleanup, vb);
goto err;
}
return 0;
err:
/* In case of errors, release planes that were already acquired */
__vb2_buf_dmabuf_put(vb);
return ret;
}
流程说明:
- ret = call_bufop(vb->vb2_queue, fill_vb2_buffer, vb, pb, planes); 填充了dma buf fd 的相关信息
- dma_buf_get(planes[plane].m.fd) 获得 dma_buf ,这里我们用ion buffer 的fd,
ion.c 的 dma_buf_ops 实现如下
static struct dma_buf_ops dma_buf_ops = {
.map_dma_buf = ion_map_dma_buf,
.unmap_dma_buf = ion_unmap_dma_buf,
.mmap = ion_mmap,
.release = ion_dma_buf_release,
.begin_cpu_access = ion_dma_buf_begin_cpu_access,
.end_cpu_access = ion_dma_buf_end_cpu_access,
.kmap_atomic = ion_dma_buf_kmap,
.kunmap_atomic = ion_dma_buf_kunmap,
.kmap = ion_dma_buf_kmap,
.kunmap = ion_dma_buf_kunmap,
};
- mem_priv = call_ptr_memop(vb, attach_dmabuf, q->alloc_devs[plane] ? : q->dev, dbuf, planes[plane].length, dma_dir); 这里的 attach_dmabuf 为 vb2_dma_sg_attach_dmabuf
static void *vb2_dma_sg_attach_dmabuf(void *alloc_ctx, struct dma_buf *dbuf,
unsigned long size, enum dma_data_direction dma_dir)
{
struct vb2_dma_sg_conf *conf = alloc_ctx;
struct vb2_dma_sg_buf *buf;
struct dma_buf_attachment *dba;
if (dbuf->size < size)
return ERR_PTR(-EFAULT);
buf = kzalloc(sizeof(*buf), GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
buf->dev = conf->dev;
/* create attachment for the dmabuf with the user device */
dba = dma_buf_attach(dbuf, buf->dev);
if (IS_ERR(dba)) {
pr_err("failed to attach dmabuf\n");
kfree(buf);
return dba;
}
buf->dma_dir = dma_dir;
buf->size = size;
buf->db_attach = dba;
return buf;
}
mem_priv 为 struct vb2_dma_sg_buf *buf;
- ret = call_memop(vb, map_dmabuf, vb->planes[plane].mem_priv); 调用vb2_dma_sg_map_dmabuf 对dma_buf 进行map 操作最终得到了struct sg_table *sgt;
static int vb2_dma_sg_map_dmabuf(void *mem_priv)
{
struct vb2_dma_sg_buf *buf = mem_priv;
struct sg_table *sgt;
if (WARN_ON(!buf->db_attach)) {
pr_err("trying to pin a non attached buffer\n");
return -EINVAL;
}
if (WARN_ON(buf->dma_sgt)) {
pr_err("dmabuf buffer is already pinned\n");
return 0;
}
/* get the associated scatterlist for this buffer */
sgt = dma_buf_map_attachment(buf->db_attach, buf->dma_dir);
if (IS_ERR(sgt)) {
pr_err("Error getting dmabuf scatterlist\n");
return -EINVAL;
}
buf->dma_sgt = sgt;
buf->vaddr = NULL;
return 0;
}
- 在实现v4l2 drv 的时候 ret = call_vb_qop(vb, buf_prepare, vb); 实现 buf_prepare
在 其中对sg_table 进行map 操作最终获得 iova 地址送给DMA进行写操作
struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
dma_map_sg(videodev,sgt->sgl,sgt->nents,DMA_BIDIRECTIONAL);
dma_adrr_t dma_addr = sg_dma_address(sgt->sgl);
特别注意的是,如果两个module都对该dma buffer 进行了map,则需要使用仿照vb2_dma_sg_dmabuf_ops_attach 中的实现在dma_map_sg 将 struct sg_table *sgt 的内容clone一份,从而避免影响了其他module的使用,同理,其他的module也需要按照此方式进行使用
static int vb2_dma_sg_dmabuf_ops_attach(struct dma_buf *dbuf, struct device *dev,
struct dma_buf_attachment *dbuf_attach)
{
struct vb2_dma_sg_attachment *attach;
unsigned int i;
struct scatterlist *rd, *wr;
struct sg_table *sgt;
struct vb2_dma_sg_buf *buf = dbuf->priv;
int ret;
attach = kzalloc(sizeof(*attach), GFP_KERNEL);
if (!attach)
return -ENOMEM;
sgt = &attach->sgt;
/* Copy the buf->base_sgt scatter list to the attachment, as we can't
* map the same scatter list to multiple attachments at the same time.
*/
ret = sg_alloc_table(sgt, buf->dma_sgt->orig_nents, GFP_KERNEL);
if (ret) {
kfree(attach);
return -ENOMEM;
}
rd = buf->dma_sgt->sgl;
wr = sgt->sgl;
for (i = 0; i < sgt->orig_nents; ++i) {
sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
rd = sg_next(rd);
wr = sg_next(wr);
}
attach->dma_dir = DMA_NONE;
dbuf_attach->priv = attach;
return 0;
}
- dqbuf 并没有对dma buffer 进行释放
- stop 或者主动释放req_bufs(0)会调用 vb2_dma_sg_unmap_dmabuf 进行操作释放资源