【genius_platform软件平台开发】第五十八讲:Linux系统之V4L2视频驱动-VIDIOC_REQBUFS向驱动申请帧缓冲代码详解

1. 概述

  • VIDIOC_REQBUFS 请求在内核空间分配视频缓冲区

  • 功能
    请求V4L2驱动分配视频缓冲区(申请V4L2视频驱动分配内存)
    V4L2是视频设备的驱动层,位于内核空间,所以通过 VIDIOC_REQBUFS控制命令申请的内存位于内核空间,应用程序不能直接访问,需要通过调用mmap内存映射函数,把内核空间内存映射到用户空间后,应用程序通过访问用户空间地址来访问内核空间。

  • 参数说明
    参数类型为 V4L2的申请缓冲区数据结构体类型 struct v4l2_requestbuffersv4l2_requestbuffers 结构中定义了缓存的数量,驱动会据此申请对应数量的视频缓存。多个缓存可以用于建立FIFO,来提高视频采集的效率。
    一般不超过5个,CAP_BUF_NUM = 4

  • 返回值说明
    执行成功时,函数返回值为 0V4L2驱动层分配好了视频缓冲区;

2. 应用层

struct v4l2_requestbuffers {  
    __u32           count;  
    __u32           type;       /* enum v4l2_buf_type */  
    __u32           memory;     /* enum v4l2_memory */  
    __u32           reserved[2];  
};  


struct v4l2_requestbuffers req;
/* 申请设备的缓存区 */
memset(&req, 0, sizeof(req));
req.count = CAP_BUF_NUM;  //申请一个拥有四个缓冲帧的缓冲区
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;

if (ioctl(dev->fd, VIDIOC_REQBUFS, &req) < 0)
{
    if (EINVAL == errno)
    {
        printf(stderr, "%s does not support "
                 "memory mapping\n", dev->dev);
        return TFAIL;
    }
    else
    {
        printf(stderr, "%s does not support "
                 "memory mapping, unknow error\n", dev->dev);
        return TFAIL;
    }
}
if (req.count < 2)
{
    printf(stderr, "Insufficient buffer memory on %s\n",
             dev->dev);
    return TFAIL;
}

3. 内核驱动

3.1 vb2_ioctl_reqbufs函数

/* vb2 ioctl helpers */

int vb2_ioctl_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p)
{
	// 获取dev
	struct video_device *vdev = video_devdata(file);
	
	// 检查队列memory类型是否一致,不一致则直接退出
	int res = vb2_verify_memory_type(vdev->queue, p->memory, p->type);
	if (res)
	    return res;
	  
	if (vb2_queue_is_busy(vdev, file))
	    return -EBUSY;
	  
	res = vb2_core_reqbufs(vdev->queue, p->memory, &p->count);
	/*
	* 注意
	* 	1. 当count=0的时候,释放所有buffer
	*/
	/* If count == 0, then the owner has released all buffers and he
	is no longer owner of the queue. Otherwise we have a new owner. */
	if (res == 0)
	    vdev->queue->owner = p->count ? file->private_data : NULL;
	return res;
}
EXPORT_SYMBOL_GPL(vb2_ioctl_reqbufs);

3.2 vb2_core_reqbufs函数

int vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, unsigned int *count)
{
        unsigned int num_buffers, allocated_buffers, num_planes = 0;
        // #define VB2_MAX_PLANES  (8)
        unsigned plane_sizes[VB2_MAX_PLANES] = { };
        int ret;
		
		// 正在取流中
        if (q->streaming) {
                dprintk(1, "streaming active\n");
                return -EBUSY;
        }
		
		/*
		* 注意
		* 1。 这里进行count=0判断,memory类型判断,删除所有buffer,所以为什么应用层会进行0 buffer的申请操作。
		*/
        if (*count == 0 || q->num_buffers != 0 || q->memory != memory) {
                /*
                 * We already have buffers allocated, so first check if they
                 * are not in use and can be freed.
                 */
                mutex_lock(&q->mmap_lock);
                if (q->memory == VB2_MEMORY_MMAP && __buffers_in_use(q)) {
                        mutex_unlock(&q->mmap_lock);
                        dprintk(1, "memory in use, cannot free\n");
                        return -EBUSY;
                }

                /*
                 * Call queue_cancel to clean up any buffers in the PREPARED or
                 * QUEUED state which is possible if buffers were prepared or
                 * queued without ever calling STREAMON.
                 */
                __vb2_queue_cancel(q);
                ret = __vb2_queue_free(q, q->num_buffers);
                mutex_unlock(&q->mmap_lock);
                if (ret)
                        return ret;

                /*
                 * In case of REQBUFS(0) return immediately without calling
                 * driver's queue_setup() callback and allocating resources.
                 */
                if (*count == 0)
                        return 0;
        }

        /*
         * Make sure the requested values and current defaults are sane.
         */
         // #define VB2_MAX_FRAME   (32)
        num_buffers = min_t(unsigned int, *count, VB2_MAX_FRAME);
        // min_buffers_needed = 2一般为2
        num_buffers = max_t(unsigned int, num_buffers, q->min_buffers_needed);
        memset(q->alloc_devs, 0, sizeof(q->alloc_devs));
        q->memory = memory;

		// 从驱动层获取到可以支持多少buffers和planes per buffer
        /*
         * Ask the driver how many buffers and planes per buffer it requires.
         * Driver also sets the size and allocator context for each plane.
         */
        ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes, plane_sizes, q->alloc_devs);
        if (ret)
                return ret;

		// 最后分配buffer
        /* Finally, allocate buffers and video memory */
        allocated_buffers =__vb2_queue_alloc(q, memory, num_buffers, num_planes, plane_sizes);
        if (allocated_buffers == 0) {
                dprintk(1, "memory allocation failed\n");
                return -ENOMEM;
        }

		// 检查是否大于最小需求buffer
        /*
         * There is no point in continuing if we can't allocate the minimum
         * number of buffers needed by this vb2_queue.
         */
        if (allocated_buffers < q->min_buffers_needed)
                ret = -ENOMEM;

        /*
         * Check if driver can handle the allocated number of buffers.
         */
        if (!ret && allocated_buffers < num_buffers) {
                num_buffers = allocated_buffers;
                /*
                 * num_planes is set by the previous queue_setup(), but since it
                 * signals to queue_setup() whether it is called from create_bufs()
                 * vs reqbufs() we zero it here to signal that queue_setup() is
                 * called for the reqbufs() case.
                 */
                num_planes = 0;

                ret = call_qop(q, queue_setup, q, &num_buffers,
                               &num_planes, plane_sizes, q->alloc_devs);

                if (!ret && allocated_buffers < num_buffers)
                        ret = -ENOMEM;

                /*
                 * Either the driver has accepted a smaller number of buffers,
                 * or .queue_setup() returned an error
                 */
        }

        mutex_lock(&q->mmap_lock);
        q->num_buffers = allocated_buffers;

        if (ret < 0) {
                /*
                 * Note: __vb2_queue_free() will subtract 'allocated_buffers'
                 * from q->num_buffers.
                 */
                __vb2_queue_free(q, allocated_buffers);
                mutex_unlock(&q->mmap_lock);
                return ret;
        }
        mutex_unlock(&q->mmap_lock);

		/*
		* 注意
		* 	1.这里对应用层传递过来的count进行了,已经分配buffer数量的更新操作。
		*/
        /*
         * Return the number of successfully allocated buffers
         * to the userspace.
         */
        *count = allocated_buffers;
        q->waiting_for_buffers = !q->is_output;

        return 0;
}
EXPORT_SYMBOL_GPL(vb2_core_reqbufs);

3.3 __vb2_queue_alloc函数

/**
 * __vb2_queue_alloc() - allocate videobuf buffer structures and (for MMAP type)
 * video buffer memory for all buffers/planes on the queue and initializes the
 * queue
 *
 * Returns the number of buffers successfully allocated.
 */
static int __vb2_queue_alloc(struct vb2_queue *q, enum vb2_memory memory,
                             unsigned int num_buffers, unsigned int num_planes,
                             const unsigned plane_sizes[VB2_MAX_PLANES])
{
        unsigned int buffer, plane;
        struct vb2_buffer *vb;
        int ret;

        /* Ensure that q->num_buffers+num_buffers is below VB2_MAX_FRAME */
        num_buffers = min_t(unsigned int, num_buffers,
                            VB2_MAX_FRAME - q->num_buffers);

		// 分配 vidoe buffer了
        for (buffer = 0; buffer < num_buffers; ++buffer) 
        {
                /* Allocate videobuf buffer structures */
                vb = kzalloc(q->buf_struct_size, GFP_KERNEL);
                if (!vb) {
                        dprintk(1, "memory alloc for buffer struct failed\n");
                        break;
                }
                
				// 设置video buffer的数据成员
                vb->state = VB2_BUF_STATE_DEQUEUED;
                vb->vb2_queue = q;
                vb->num_planes = num_planes;
                vb->index = q->num_buffers + buffer;
                vb->type = q->type;
                vb->memory = memory;
                for (plane = 0; plane < num_planes; ++plane) {
                        vb->planes[plane].length = plane_sizes[plane];
                        vb->planes[plane].min_length = plane_sizes[plane];
                }
                /*
                * 注意
                * 	1. 这里使用的idx索引
                */
                q->bufs[vb->index] = vb;

                /* Allocate video buffer memory for the MMAP type */
                if (memory == VB2_MEMORY_MMAP)
                {
                        ret = __vb2_buf_mem_alloc(vb);
                        if (ret) {
                                dprintk(1, "failed allocating memory for buffer %d\n",
                                        buffer);
                                q->bufs[vb->index] = NULL;
                                kfree(vb);
                                break;
                        }
                        __setup_offsets(vb);
                        /*
                         * Call the driver-provided buffer initialization
                         * callback, if given. An error in initialization
                         * results in queue setup failure.
                         */
                        ret = call_vb_qop(vb, buf_init, vb);
                        if (ret) {
                                dprintk(1, "buffer %d %p initialization failed\n",
                                        buffer, vb);
                                __vb2_buf_mem_free(vb);
                                q->bufs[vb->index] = NULL;
                                kfree(vb);
                                break;
                        }
                }
        }

        dprintk(1, "allocated %d buffers, %d plane(s) each\n",
                        buffer, num_planes);

        return buffer;
}

3.4 __setup_offsets函数

// 其实主要就是内存模块的对齐 page
/**
 * __setup_offsets() - setup unique offsets ("cookies") for every plane in
 * the buffer.
 */
static void __setup_offsets(struct vb2_buffer *vb)
{
        struct vb2_queue *q = vb->vb2_queue;
        unsigned int plane;
        unsigned long off = 0;

        if (vb->index) 
        {
                struct vb2_buffer *prev = q->bufs[vb->index - 1];
                struct vb2_plane *p = &prev->planes[prev->num_planes - 1];

                off = PAGE_ALIGN(p->m.offset + p->length);
        }

        for (plane = 0; plane < vb->num_planes; ++plane) 
        {
                vb->planes[plane].m.offset = off;

                dprintk(3, "buffer %d, plane %d offset 0x%08lx\n",
                                vb->index, plane, off);

                off += vb->planes[plane].length;
                off = PAGE_ALIGN(off);
        }
}

3.5 __vb2_buf_mem_alloc函数

/**
 * __vb2_buf_mem_alloc() - allocate video memory for the given buffer
 */
static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
{
        struct vb2_queue *q = vb->vb2_queue;
        void *mem_priv;
        int plane;
        int ret = -ENOMEM;

        /*
         * Allocate memory for all planes in this buffer
         * NOTE: mmapped areas should be page aligned
         */
        for (plane = 0; plane < vb->num_planes; ++plane) 
        {
        		// page对齐
                unsigned long size = PAGE_ALIGN(vb->planes[plane].length);
                
				// 内核mmap
                mem_priv = call_ptr_memop(vb, alloc,
                                q->alloc_devs[plane] ? : q->dev,
                                q->dma_attrs, size, q->dma_dir, q->gfp_flags);
                if (IS_ERR_OR_NULL(mem_priv)) {
                        if (mem_priv)
                                ret = PTR_ERR(mem_priv);
                        goto free;
                }

                /* Associate allocator private data with this plane */
                vb->planes[plane].mem_priv = mem_priv;
        }

        return 0;
free:
        /* Free already allocated memory if one of the allocations failed */
        for (; plane > 0; --plane) {
                call_void_memop(vb, put, vb->planes[plane - 1].mem_priv);
                vb->planes[plane - 1].mem_priv = NULL;
        }

        return ret;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

隨意的風

如果你觉得有帮助,期待你的打赏

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

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

打赏作者

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

抵扣说明:

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

余额充值