Linux V4L2驱动框架分析之(三):v4l2设备的缓存管理

news/2024/7/7 12:40:41

系列文章
Linux V4L2驱动框架分析之(一):架构介绍
Linux V4L2驱动框架分析之(二):平台v4l2设备驱动
Linux V4L2驱动框架分析之(三):v4l2设备的缓存管理
Linux V4L2驱动框架分析之(四):sensor驱动

v4l2设备读取数据的方式有两种,一种是read方式,一种是streaming方式。read方式很容易理解,就是通过read函数读取,而streaming方式是在内核空间中维护一个缓存队列,然后将内存映射到用户空间,应用读取图像数据就是一个不断地出队列和入队列的过程,如下图所示:
在这里插入图片描述
使用streaming方式,需要管理多块缓冲,内核通过vb2_queue来管理,vb2_queue即缓冲队列。

应用程序查询设备功能,判断设备是否支持streaming方式:

if (ioctl(fd, VIDIOC_QUERYCAP, cap) < 0)
{
    printf("ERR(%s):VIDIOC_QUERYCAP failed\n", __func__);
    return -1;
}

传入的cap参数是一个truct v4l2_capability结构体的指针,该结构体的定义如下:

/* include/uapi/linux/videodev2.h */
struct v4l2_capability {
	__u8	driver[16];
	__u8	card[32];
	__u8	bus_info[32];
	__u32   version;
	__u32	capabilities;
	__u32	device_caps;
	__u32	reserved[3];
};

其中最重要的是capabilities字段,这个字段标记着v4l2设备的功能,capabilities有以下部分标记位:
在这里插入图片描述
struct video_device结构体有一成员queue:

struct video_device
{
	......
	struct vb2_queue *queue;
	......
};

采用streaming方式时,平台v4l2设备驱动需要设置与初始化该成员。

struct vb2_queue结构体定义如下:

struct vb2_queue {
	
	......

	const struct vb2_ops		*ops;
	const struct vb2_mem_ops	*mem_ops; 
	const struct vb2_buf_ops	*buf_ops;

	......

	struct vb2_buffer		*bufs[VB2_MAX_FRAME];
	unsigned int			num_buffers;

	struct list_head		queued_list;
	unsigned int			queued_count;


	struct list_head		done_list;
	wait_queue_head_t		done_wq;
	
	......
};

一般在v4l2_file_operations的open回调里设置并调用vb2_queue_init函数初始化vb2_queue,最关健的要设置vb2_queue的ops、mem_ops成员。

vb2_queue的mem_ops成员里有申请、释放和映射缓存的等回调函数,数据类型为struct vb2_mem_ops,定义如下:

struct vb2_mem_ops {

	//申请缓存
	void		*(*alloc)(struct device *dev, unsigned long attrs,
				  unsigned long size,
				  enum dma_data_direction dma_dir,
				  gfp_t gfp_flags);
	void		(*put)(void *buf_priv);
	struct dma_buf *(*get_dmabuf)(void *buf_priv, unsigned long flags);

	void		*(*get_userptr)(struct device *dev, unsigned long vaddr,
					unsigned long size,
					enum dma_data_direction dma_dir);
	void		(*put_userptr)(void *buf_priv);

	void		(*prepare)(void *buf_priv);
	void		(*finish)(void *buf_priv);

	void		*(*attach_dmabuf)(struct device *dev,
					  struct dma_buf *dbuf,
					  unsigned long size,
					  enum dma_data_direction dma_dir);
	void		(*detach_dmabuf)(void *buf_priv);
	int		(*map_dmabuf)(void *buf_priv);
	void		(*unmap_dmabuf)(void *buf_priv);

	void		*(*vaddr)(void *buf_priv);
	void		*(*cookie)(void *buf_priv);

	unsigned int	(*num_users)(void *buf_priv);

	//映射缓存
	int		(*mmap)(void *buf_priv, struct vm_area_struct *vma);
};

vb2_queue支持这三种形式缓存,内核提供了三种vb2_mem_ops操作函数集:

/* drivers/media/v4l2-core/videobuf2-vmalloc.c 
   分配的缓冲区,虚拟地址连续,物理地址不一定连续
 */
const struct vb2_mem_ops vb2_vmalloc_memops = {
	.alloc		= vb2_vmalloc_alloc,
	.put		= vb2_vmalloc_put,
	.get_userptr	= vb2_vmalloc_get_userptr,
	.put_userptr	= vb2_vmalloc_put_userptr,
#ifdef CONFIG_HAS_DMA
	.get_dmabuf	= vb2_vmalloc_get_dmabuf,
#endif
	.map_dmabuf	= vb2_vmalloc_map_dmabuf,
	.unmap_dmabuf	= vb2_vmalloc_unmap_dmabuf,
	.attach_dmabuf	= vb2_vmalloc_attach_dmabuf,
	.detach_dmabuf	= vb2_vmalloc_detach_dmabuf,
	.vaddr		= vb2_vmalloc_vaddr,
	.mmap		= vb2_vmalloc_mmap,
	.num_users	= vb2_vmalloc_num_users,
};

/* drivers/media/v4l2-core/videobuf2-dma-sg.c 
   对于支持Scatter/Gather的DMA,可使用vb2_dma_sg_memops 
 */
const struct vb2_mem_ops vb2_dma_sg_memops = {
	.alloc		= vb2_dma_sg_alloc,
	.put		= vb2_dma_sg_put,
	.get_userptr	= vb2_dma_sg_get_userptr,
	.put_userptr	= vb2_dma_sg_put_userptr,
	.prepare	= vb2_dma_sg_prepare,
	.finish		= vb2_dma_sg_finish,
	.vaddr		= vb2_dma_sg_vaddr,
	.mmap		= vb2_dma_sg_mmap,
	.num_users	= vb2_dma_sg_num_users,
	.get_dmabuf	= vb2_dma_sg_get_dmabuf,
	.map_dmabuf	= vb2_dma_sg_map_dmabuf,
	.unmap_dmabuf	= vb2_dma_sg_unmap_dmabuf,
	.attach_dmabuf	= vb2_dma_sg_attach_dmabuf,
	.detach_dmabuf	= vb2_dma_sg_detach_dmabuf,
	.cookie		= vb2_dma_sg_cookie,
};

/* drivers/media/v4l2-core/videobuf2-dma-contig.c */
const struct vb2_mem_ops vb2_dma_contig_memops = {
	.alloc		= vb2_dc_alloc,
	.put		= vb2_dc_put,
	.get_dmabuf	= vb2_dc_get_dmabuf,
	.cookie		= vb2_dc_cookie,
	.vaddr		= vb2_dc_vaddr,
	.mmap		= vb2_dc_mmap,
	.get_userptr	= vb2_dc_get_userptr,
	.put_userptr	= vb2_dc_put_userptr,
	.prepare	= vb2_dc_prepare,
	.finish		= vb2_dc_finish,
	.map_dmabuf	= vb2_dc_map_dmabuf,
	.unmap_dmabuf	= vb2_dc_unmap_dmabuf,
	.attach_dmabuf	= vb2_dc_attach_dmabuf,
	.detach_dmabuf	= vb2_dc_detach_dmabuf,
	.num_users	= vb2_dc_num_users,
};

根据具体设备使用缓存区的方式,选择对应的vb2_mem_ops。

应用程序申请缓存:

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;
}

到内核态的执行流程:
在这里插入图片描述
申请缓冲之后,把缓冲放入队列:

struct v4l2_buffer v4l2_buffer;

for(i = 0; i < nr_bufs; i++)
{
	memset(&v4l2_buffer, 0, sizeof(struct v4l2_buffer));
	v4l2_buffer.index = i; //想要放入队列的缓存
	v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	v4l2_buffer.memory = V4L2_MEMORY_MMAP;	

    ret = ioctl(fd, VIDIOC_QBUF, &v4l2_buffer);
    if(ret < 0)
    {
        printf("Unable to queue buffer.\n");
        return -1;
    }
}

到内核态的执行流程:
在这里插入图片描述
把缓冲放入队列之后就是映射缓存了。因为如果使用read方式读取的话,图像数据是从内核空间拷贝会应用空间,而一副图像的数据一般来讲是比较大的,所以效率会比较低。而如果使用映射的方式,讲内核空间的内存应用到用户空间,那么用户空间读取数据就想在操作内存一样,不需要经过内核空间到用户空间的拷贝,大大提高效率。

struct v4l2_buffer v4l2_buffer;
void* addr;

memset(&v4l2_buffer, 0, sizeof(struct v4l2_buffer));
v4l2_buffer.index = i; //想要查询的缓存
v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_buffer.memory = V4L2_MEMORY_MMAP;

/* 查询缓存信息 */
ret = ioctl(fd, VIDIOC_QUERYBUF, &v4l2_buffer);
if(ret < 0)
{
    printf("Unable to query buffer.\n");
    return -1;
}

/* 映射 */
addr = mmap(NULL /* start anywhere */ ,
            v4l2_buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED,
            fd, v4l2_buffer.m.offset);

获取图像数据其实就是一个不断地入队列和出队列地过程,在出队列前要调用poll等待数据准备完成,应用程序poll操作到内核态:
在这里插入图片描述
具体的驱动程序在获得到图像数据后,需要从struct vb2_buffer的queued_list链表得到一个struct vb2_buffer,把数据填入vb2_buffer,之后将该vb2_buffer挂入到struct vb2_buffer的done_list链表,最终唤醒进程。

数据准备完成,进缓存出队:
在这里插入图片描述
如果是映射缓存,出队后,应用程序可直接操作缓存里的图像数据,处理完数据之后再次把缓存放入队列。


http://www.niftyadmin.cn/n/3657251.html

相关文章

MS CRM Callouts Tip - 在Callouts中调用log4net

CRM中的Callouts未处理的异常信息会被CRM Server记录在Windows的Event Log中&#xff0c;如果要通过log4net实现异常记录&#xff0c;会遇到一个问题 - 在WinForm 程序或者 ASP.NET程序中使用log4net&#xff0c;需要在config文件中配置 log4net 所需的配置节。Callouts作为Cla…

Linux V4L2驱动框架分析之(四):sensor驱动

系列文章&#xff1a; Linux V4L2驱动框架分析之&#xff08;一&#xff09;&#xff1a;架构介绍 Linux V4L2驱动框架分析之&#xff08;二&#xff09;&#xff1a;平台v4l2设备驱动 Linux V4L2驱动框架分析之&#xff08;三&#xff09;&#xff1a;v4l2设备的缓存管理 Linu…

[概念] 激励理论-海兹伯格激励理论(Herzberg's theory of motivation)

详见http://www.netmba.com/mgmt/ob/motivation/herzberg/Herzberg found that the factors causing job satisfaction (and presumably motivation) were different from those causing job dissatisfaction. He developed the motivation-hygiene theory to explain these re…

项目经理的角色(Roles of Project Manager)

来自 http://www.yancy.org/research/project_management/human_resources.html * Integrator o PM is the most likely person who can view both the project and the way it fits into the overall plan for the organization. o Must coordinate the …

组织结构 - 职能型,矩阵型和项目型

PMBOK(2004 3rd 英)P28中介绍了几种组织结够&#xff0c;包括职能型(Functional) &#xff0c;矩阵型(Matrix)和项目型(Projectized) 。其中矩阵型又分为Weak Matrix,Balanced Matrix和Strong Matrix. 书中只是从项目经理职权的角度比较了这几种组织结构的不同。下面是一些补充…

Project Management 101 lessons

101这个数字在西方也许有什么特殊的含义 - 因为知道asp101.com&#xff0c;今天发现了Project Management 101- http://www.suite101.com/course.cfm/17517。 好像二战中美国有个101空降师&#xff0c;王牌部队&#xff0c;所以101代表王牌&#xff1f;作者说世界上有三种人&am…

FastDFS配置文件详解之tracker.conf

本文转自 http://bbs.chinaunix.net/thread-1941456-1-1.html&#xff0c;如果您也转载&#xff0c;请注明出处。 ----------------------------------------------------------------------------------------------------------------------------------------------------…

[概念] 质量规划工具 - Affinity Diagram

PMBOK (2004 3rd 英) P186中提到了一些质量规划工具的名称&#xff0c;并没有详细说明。其中提到了Affinity Diagram,详细如下&#xff1a;http://www.skymark.com/resources/tools/affinity_diagram.asp概念和作用The affinity diagram, or KJ method (after its author, Kawa…