从应用调用vivi驱动分析v4l2 -- 映射 mmap

本文详细解析了Linux v4l2框架中关于缓存设置和mmap操作的过程,从VIDIOC_QUERYBUF到v4l2_fops的mmap,再到vb2_mmap的实现。通过offset找到对应的buffer及plane,理解mmap在内存映射中的作用。内容适合熟悉Linux驱动和视频处理的技术人员阅读。
摘要由CSDN通过智能技术生成

Linux v4l2架构学习总链接

vivi代码

v4l2测试代码

step 5 : 设置缓存

映射

mmap一般都是配合VIDIOC_QUERYBUF使用

    for (n_buffers = 0; n_buffers < req.count; ++n_buffers) 
	{
		struct v4l2_buffer buf;
		CLEAR (buf);
		buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory      = V4L2_MEMORY_MMAP;
		buf.index       = n_buffers;

		if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buf))
			printf ("VIDIOC_QUERYBUF error\n");

		buffers[n_buffers].length = buf.length;
		buffers[n_buffers].start =
			mmap (NULL /* start anywhere */,
					buf.length,
					PROT_READ | PROT_WRITE /* required */,
					MAP_SHARED /* recommended */,
					fd, buf.m.offset);

		if (MAP_FAILED == buffers[n_buffers].start)
			printf ("mmap failed\n");
	}

在这里又一次看到了offset的身影,这一次可以搞定它。

mmap对应驱动的 videox的fops也就是 v4l2_fops的mmap

static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
{
	struct video_device *vdev = video_devdata(filp);
	int ret = -ENODEV;

	if (!vdev->fops->mmap)
		return -ENODEV;
	if (video_is_registered(vdev))
		ret = vdev->fops->mmap(filp, vm);
	if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP)
		dprintk("%s: mmap (%d)\n",
			video_device_node_name(vdev), ret);
	return ret;
}

vdev->fops对应

vivi_fops.mmap 也就是vivi_mmap

static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
{
	struct vivi_dev *dev = video_drvdata(file);
	int ret;
	ret = vb2_mmap(&dev->vb_vidq, vma);
	return ret;
}

分析vb2_mmap

int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma)
{
	unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
	struct vb2_buffer *vb;
	unsigned int buffer = 0, plane = 0;
	int ret;
	unsigned long length;


        /*
         * 对于下面的判断条件不深入分析,主要不是很了解那些标志位
         */


	if (q->memory != VB2_MEMORY_MMAP) {
		dprintk(1, "queue is not currently set up for mmap\n");
		return -EINVAL;
	}

	/*
	 * Check memory area access mode.
	 */
	if (!(vma->vm_flags & VM_SHARED)) {
		dprintk(1, "invalid vma flags, VM_SHARED needed\n");
		return -EINVAL;
	}
	if (q->is_output) {
		if (!(vma->vm_flags & VM_WRITE)) {
			dprintk(1, "invalid vma flags, VM_WRITE needed\n");
			return -EINVAL;
		}
	} else {
		if (!(vma->vm_flags & VM_READ)) {
			dprintk(1, "invalid vma flags, VM_READ needed\n");
			return -EINVAL;
		}
	}

	mutex_lock(&q->mmap_lock);

        

        /*
         * 暂且认为非active,也就是q->fileio = 0
         */

    
	if (vb2_fileio_is_active(q)) {
		dprintk(1, "mmap: file io in progress\n");
		ret = -EBUSY;
		goto unlock;
	}



        /*
         * off就是offset的值
         * __find_plane_by_offset
         * 这个函数非常简单,核心思想就是通过offset的值找到对应的buffer
         * 单独分析见下面
         */


	/*
	 * Find the plane corresponding to the offset passed by userspace.
	 */
	ret = __find_plane_by_offset(q, off, &buffer, &plane);
	if (ret)
		goto unlock;

	vb = q->bufs[buffer];

	/*
	 * MMAP requires page_aligned buffers.
	 * The buffer length was page_aligned at __vb2_buf_mem_alloc(),
	 * so, we need to do the same here.
	 */


        /*
         * mmap需要页对齐,所以这里将length进行一个页对齐的操作
         * 同时计算申请的范围是否有效
         */


	length = PAGE_ALIGN(vb->planes[plane].length);
	if (length < (vma->vm_end - vma->vm_start)) {
		dprintk(1,
			"MMAP invalid, as it would overflow buffer length\n");
		ret = -EINVAL;
		goto unlock;
	}


        /*
         * 对应vb->vb2_queue->mem_ops->mmap
         * 也就是vb2_vmalloc_memops的mmap 
         * vb2_vmalloc_mmap 这里就是映射相关的了,不再追踪,offset的作用已经知道了
         * 最重要的是mmap我暂时也搞不明白
         */


	ret = call_memop(vb, mmap, vb->planes[plane].mem_priv, vma);

unlock:
	mutex_unlock(&q->mmap_lock);
	if (ret)
		return ret;

	dprintk(3, "buffer %d, plane %d successfully mapped\n", buffer, plane);
	return 0;
}

分析一下上面函数中的__find_plane_by_offset

static int __find_plane_by_offset(struct vb2_queue *q, unsigned long off,
			unsigned int *_buffer, unsigned int *_plane)
{
	struct vb2_buffer *vb;
	unsigned int buffer, plane;

	/*
	 * Go over all buffers and their planes, comparing the given offset
	 * with an offset assigned to each plane. If a match is found,
	 * return its buffer and plane numbers.
	 */
	for (buffer = 0; buffer < q->num_buffers; ++buffer) {
		vb = q->bufs[buffer];

		for (plane = 0; plane < vb->num_planes; ++plane) {
			if (vb->planes[plane].m.offset == off) {
				*_buffer = buffer;
				*_plane = plane;
				return 0;
			}
		}
	}

	return -EINVAL;
}

其实很明显的可以看出来,就是通过offset找到对应的buffer及plane的值

对应的应用程序可以这么写

    struct buffer {
        void * start;
        size_t length;
    };

    struct buffer *buffers = NULL;

    buffers = calloc (req.count, sizeof (*buffers));

    for(i = 0; i < req.count; i++) {
        memset(&buf, 0, sizeof(buf));
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = i;
        if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buf)) {
            printf("Querybuf fail\n");
            goto err;
        }

        printf("buffer[%d]: length = %d, offset = %d\n", i, buf.length, buf.m.offset);
        buffers[i].length = buf.length;
        buffers[i].start =
            mmap (NULL /* start anywhere */,
                    buf.length,
                    PROT_READ | PROT_WRITE /* required */,
                    MAP_SHARED /* recommended */,
                    fd, buf.m.offset);

        if (MAP_FAILED == buffers[i].start)
            printf ("mmap failed\n");
    } 

buffers用于记录mmap后的地址

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dianlong_lee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值