从vivi学习V4L2架构(六):申请存放摄像头数据内存

申请内存

 struct v4l2_requestbuffers
 {
    __u32 count;                    // 缓冲区内缓冲帧的数目
    enum v4l2_buf_type type;        // 缓冲帧数据格式
     enum v4l2_memorymemory;         // 区别是内存映射还是用户指针方式
     __u32 reserved[2];
};

struct v4l2_requestbuffers req;
 
req.count = nr_bufs; //缓存数量
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;//内存映射方式
 
if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0)
{
    printf("ERR(%s):VIDIOC_REQBUFS failed\n", __func__);
    return -1;
}

同样会调用到__video_do_ioctl

//drivers\media\video\v4l2-ioctl.c
static long __video_do_ioctl(struct file *file,
		unsigned int cmd, void *arg)
{
... ...
	case VIDIOC_REQBUFS:
	{
		struct v4l2_requestbuffers *p = arg;

		if (!ops->vidioc_reqbufs)
			break;
        //根据上层传来的V4L2_BUF_TYPE_VIDEO_CAPTURE判断一下,底层的驱动
        //是否实现了vidioc_g_fmt_vid_cap或vidioc_g_fmt_vid_cap_mplane回调函数
		ret = check_fmt(ops, p->type);
		if (ret)
			break;

		if (p->type < V4L2_BUF_TYPE_PRIVATE)
			CLEAR_AFTER_FIELD(p, memory);
//下面就是申请buffer的函数了
		ret = ops->vidioc_reqbufs(file, fh, p);
		dbgarg(cmd, "count=%d, type=%s, memory=%s\n",
				p->count,
				prt_names(p->type, v4l2_type_names),
				prt_names(p->memory, v4l2_memory_names));
		break;
	}
... ...
}

下面看一下vidioc_reqbufs的定义

static int vidioc_reqbufs(struct file *file, void *priv,
			  struct v4l2_requestbuffers *p)
{
	struct vivi_dev *dev = video_drvdata(file);
	return vb2_reqbufs(&dev->vb_vidq, p);
}

//下面vb2_reqbufs的函数有点长,中间有省略部分
int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
{
	unsigned int num_buffers, num_planes;
	unsigned long plane_sizes[VIDEO_MAX_PLANES];
	int ret = 0;

    //此时q->fileio应该为0,条件不成立跳过
	if (q->fileio) {
		dprintk(1, "reqbufs: file io in progress\n");
		return -EBUSY;
	}
    //目前这个只支持V4L2_MEMORY_MMAP和V4L2_MEMORY_USERPTR
    //上层传入的时V4L2_MEMORY_MMAP, 所以跳过这个if
	if (req->memory != V4L2_MEMORY_MMAP
			&& req->memory != V4L2_MEMORY_USERPTR) {
		dprintk(1, "reqbufs: unsupported memory type\n");
		return -EINVAL;
	}
    //判断vb2_queue->type是否是请求的req->type
    //vivi驱动初始化时设置为了V4L2_BUF_TYPE_VIDEO_CAPTURE
    //所以下面条件不成立跳过
	if (req->type != q->type) {
		dprintk(1, "reqbufs: requested type is incorrect\n");
		return -EINVAL;
	}
    //当前vb2_queue数据流的状态,这里还没申请所以这个状态应是0
	if (q->streaming) {
		dprintk(1, "reqbufs: streaming active\n");
		return -EBUSY;
	}

	/*
	 * Make sure all the required memory ops for given memory type
	 * are available.
	 */
    //判断buffer申请类型V4L2_MEMORY_MMAP时,所需的回调函数是否已经初始化
    //q->mem_ops->alloc, q->mem_ops->put和q->mem_ops->mmap
    //这些回调函数时在vivi驱动初始化时赋值的q->mem_ops = &vb2_vmalloc_memops;
	if (req->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
		dprintk(1, "reqbufs: MMAP for current setup unsupported\n");
		return -EINVAL;
	}

	if (req->memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) {
		dprintk(1, "reqbufs: USERPTR for current setup unsupported\n");
		return -EINVAL;
	}

	if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) {
		/*
		 * We already have buffers allocated, so first check if they
		 * are not in use and can be freed.
		 */
		if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) {
			dprintk(1, "reqbufs: memory in use, cannot free\n");
			return -EBUSY;
		}

		__vb2_queue_free(q);

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

	/*
	 * Make sure the requested values and current defaults are sane.
	 */
    //取应用申请的buffer数量和VIDEO_MAX_FRAME中较小的值
	num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME);
	memset(plane_sizes, 0, sizeof(plane_sizes));
	memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
	q->memory = req->memory;

	/*
	 * Ask the driver how many buffers and planes per buffer it requires.
	 * Driver also sets the size and allocator context for each plane.
	 */
    //下面调用vb2_queue.ops.queue_setup函数,在vivi驱动初始化时赋值
    //q->ops = &vivi_video_qops;queue_setup主要根据分辨率和申请的
    //buffer数来计算num_planes的数量,plane_szies,并重新调整buffer数量
	ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
		       plane_sizes, q->alloc_ctx);
	if (ret)
		return ret;

	/* Finally, allocate buffers and video memory */
    //下面函数会根据num_buffers, num_planes, plane_sizes,先初始化vb2_buffer结构
    //每个buffer对应一个vb2_buffer描述结构,然后调用vb2_queue.mem_ops中的
    //alloc函数为具体的摄像头数据申请存放空间
	ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes,
				plane_sizes);
	if (ret == 0) {
		dprintk(1, "Memory allocation failed\n");
		return -ENOMEM;
	}

	/*
	 * Check if driver can handle the allocated number of buffers.
	 */
	if (ret < num_buffers) {
		unsigned int orig_num_buffers;

		orig_num_buffers = num_buffers = ret;
		ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes,
			       plane_sizes, q->alloc_ctx);
		if (ret)
			goto free_mem;

		if (orig_num_buffers < num_buffers) {
			ret = -ENOMEM;
			goto free_mem;
		}

		/*
		 * Ok, driver accepted smaller number of buffers.
		 */
		ret = num_buffers;
	}

	/*
	 * Return the number of successfully allocated buffers
	 * to the userspace.
	 */
	req->count = ret;

	return 0;

free_mem:
	__vb2_queue_free(q);
	return ret;
}
EXPORT_SYMBOL_GPL(vb2_reqbufs);

下面接着分析queue_setup和__vb2_queue_alloc函数。在这之前先贴一下vb2_queue.ops和vb2_queue.mem_ops的初始化时的函数

q->ops = &vivi_video_qops;
q->mem_ops = &vb2_vmalloc_memops;
static struct vb2_ops vivi_video_qops = {
	.queue_setup		= queue_setup,
	.buf_init		= buffer_init,
	.buf_prepare		= buffer_prepare,
	.buf_finish		= buffer_finish,
	.buf_cleanup		= buffer_cleanup,
	.buf_queue		= buffer_queue,
	.start_streaming	= start_streaming,
	.stop_streaming		= stop_streaming,
	.wait_prepare		= vivi_unlock,
	.wait_finish		= vivi_lock,
};
const struct vb2_mem_ops vb2_vmalloc_memops = {
	.alloc		= vb2_vmalloc_alloc,
	.put		= vb2_vmalloc_put,
	.vaddr		= vb2_vmalloc_vaddr,
	.mmap		= vb2_vmalloc_mmap,
	.num_users	= vb2_vmalloc_num_users,
};

 看一下queue_setup具体干了什么事

static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
				unsigned int *nplanes, unsigned long sizes[],
				void *alloc_ctxs[])
{
	struct vivi_dev *dev = vb2_get_drv_priv(vq);
	unsigned long size;

    //下面dev->width和height就是上一篇设置图片格式时设置下来的。
	size = dev->width * dev->height * 2;
    //如果buffer count为0,则默认为32个buffer count
	if (0 == *nbuffers)
		*nbuffers = 32;
    //计算大小,如果超过了 vid_limit * 1024 * 1024,则较少buffer的数量
	while (size * *nbuffers > vid_limit * 1024 * 1024)
		(*nbuffers)--;

    //vivi默认只有一个plane
	*nplanes = 1;

    //把size赋值给plane_sizes
	sizes[0] = size;

	/*
	 * videobuf2-vmalloc allocator is context-less so no need to set
	 * alloc_ctxs array.
	 */

	dprintk(dev, 1, "%s, count=%d, size=%ld\n", __func__,
		*nbuffers, size);

	return 0;
}

__vb2_queue_alloc

static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
			     unsigned int num_buffers, unsigned int num_planes,
			     unsigned long plane_sizes[])
{
	unsigned int buffer;
	struct vb2_buffer *vb;
	int ret;

	for (buffer = 0; buffer < num_buffers; ++buffer) {
		/* Allocate videobuf buffer structures */
        //根据q->buf_struct_size去分配vb2_buffer的空间
		vb = kzalloc(q->buf_struct_size, GFP_KERNEL);
		if (!vb) {
			dprintk(1, "Memory alloc for buffer struct failed\n");
			break;
		}

		/* Length stores number of planes for multiplanar buffers */
		if (V4L2_TYPE_IS_MULTIPLANAR(q->type))
			vb->v4l2_buf.length = num_planes;
        //每个vb2_buffer的一些状态赋值

		vb->state = VB2_BUF_STATE_DEQUEUED;
		vb->vb2_queue = q;
		vb->num_planes = num_planes;
		vb->v4l2_buf.index = buffer;
		vb->v4l2_buf.type = q->type;
		vb->v4l2_buf.memory = memory;

		/* Allocate video buffer memory for the MMAP type */
		if (memory == V4L2_MEMORY_MMAP) {
            //下面的函数要为画面数据分配空间
			ret = __vb2_buf_mem_alloc(vb, plane_sizes);
			if (ret) {
				dprintk(1, "Failed allocating memory for "
						"buffer %d\n", buffer);
				kfree(vb);
				break;
			}
			/*
			 * Call the driver-provided buffer initialization
			 * callback, if given. An error in initialization
			 * results in queue setup failure.
			 */
            //vivi驱动定义的buf_init为空,什么也没做
			ret = call_qop(q, buf_init, vb);
			if (ret) {
				dprintk(1, "Buffer %d %p initialization"
					" failed\n", buffer, vb);
				__vb2_buf_mem_free(vb);
				kfree(vb);
				break;
			}
		}
        
        //把vb2_buffer赋值给vb2_queue->bufs数组,用vb2_queue去管理vb2_buffer
		q->bufs[buffer] = vb;
	}

	q->num_buffers = buffer;

	__setup_offsets(q);

	dprintk(1, "Allocated %d buffers, %d plane(s) each\n",
			q->num_buffers, num_planes);

	return buffer;
}

__vb2_buf_mem_alloc

static int __vb2_buf_mem_alloc(struct vb2_buffer *vb,
				unsigned long *plane_sizes)
{
	struct vb2_queue *q = vb->vb2_queue;
	void *mem_priv;
	int plane;

	/* Allocate memory for all planes in this buffer */
    //根据plane的数量去调用vb2_queue.mem_ops的alloc函数去分配空间
	for (plane = 0; plane < vb->num_planes; ++plane) {
		mem_priv = call_memop(q, plane, alloc, q->alloc_ctx[plane],
					plane_sizes[plane]);
		if (IS_ERR_OR_NULL(mem_priv))
			goto free;

		/* Associate allocator private data with this plane */
        //分配的空间保存在vb2_buffer->planes数组的mem_priv指针里
		vb->planes[plane].mem_priv = mem_priv;
		vb->v4l2_planes[plane].length = plane_sizes[plane];
	}

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

	return -ENOMEM;
}

vb2_queue.mem_ops.alloc具体函数就是前面的vb2_vmalloc_alloc,看一下他具体的定义

static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size)
{
	struct vb2_vmalloc_buf *buf;

	buf = kzalloc(sizeof *buf, GFP_KERNEL);
	if (!buf)
		return NULL;

	buf->size = size;
    // allocate zeroed virtually contiguous memory for userspace
    //The resulting memory area is zeroed so it can be mapped to userspace
    //without leaking data
	buf->vaddr = vmalloc_user(buf->size);
	buf->handler.refcount = &buf->refcount;
	buf->handler.put = vb2_vmalloc_put;
	buf->handler.arg = buf;

	if (!buf->vaddr) {
		printk(KERN_ERR "vmalloc of size %ld failed\n", buf->size);
		kfree(buf);
		return NULL;
	}

	atomic_inc(&buf->refcount);
	printk(KERN_DEBUG "Allocated vmalloc buffer of size %ld at vaddr=%p\n",
			buf->size, buf->vaddr);

	return buf;
}

最后看一下__setup_offsets(q);mmap时需要用到里面的offset, 之前漏看了。

/**
 * __setup_offsets() - setup unique offsets ("cookies") for every plane in
 * every buffer on the queue
 */
static void __setup_offsets(struct vb2_queue *q)
{
	unsigned int buffer, plane;
	struct vb2_buffer *vb;
	unsigned long off = 0;

	for (buffer = 0; buffer < q->num_buffers; ++buffer) {
		vb = q->bufs[buffer];
		if (!vb)
			continue;
        
        //对应单个plane,其实mem_offset=0
		for (plane = 0; plane < vb->num_planes; ++plane) {
			vb->v4l2_planes[plane].m.mem_offset = off;

			dprintk(3, "Buffer %d, plane %d offset 0x%08lx\n",
					buffer, plane, off);

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

总结:申请buffer时,根据上层申请的buffer数量,驱动层创建对应数目的vb2_buffer、num_planes、并设置对应的state, 所属哪个vb2_queue。还有设置vb2_buffer对应的v4l2_buf.index、v4l2_buf.type、v4l2_buf.memory申请内存的方式(mmap, userptr)。
用vb2_queue->bufs[]存放对应的vb2_buffer。申请的内存空间放到vb2_buffer->planes[plane].mem_priv.vaddr;vb2_buffer结构里面的v4l2_buffer主要用来描述当前vb2_buffer申请的空间的index, type, timestamp, memory类型等等。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值