Linux V4L2驱动框架分析之(二):平台v4l2设备驱动

news/2024/7/4 23:32:11

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

平台V4L2设备驱动的工作:根据平台自身的特性实现与平台相关的V4L2驱动部分,最主要的是包括设置并注册video_device和v4l2_device。

来看看v4l2_device结构体,定义如下:

struct v4l2_device {
	
	struct device *dev;
	......

	//subdevs链表,属于该v4l2_device的子设备会挂入到该链表
	struct list_head subdevs;

	spinlock_t lock;

	//v4l2_device nam
	char name[V4L2_DEVICE_NAME_SIZE];

	void (*notify)(struct v4l2_subdev *sd,
			unsigned int notification, void *arg);

	struct v4l2_ctrl_handler *ctrl_handler;

	......
	
	struct kref ref;

	//当ref为0,调用该函数释放v4l2_device 
	void (*release)(struct v4l2_device *v4l2_dev);
};

设置好v4l2_device后,调用v4l2_device_register函数进行注册:

int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
{
		
	......

	//初始化链表、锁等
	INIT_LIST_HEAD(&v4l2_dev->subdevs);
	spin_lock_init(&v4l2_dev->lock);
	mutex_init(&v4l2_dev->ioctl_lock);
	v4l2_prio_init(&v4l2_dev->prio);
	kref_init(&v4l2_dev->ref);
	get_device(dev);
	v4l2_dev->dev = dev;

	......

	/* Set name to driver name + device name if it is empty. */
	if (!v4l2_dev->name[0])
		snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
			dev->driver->name, dev_name(dev));

	if (!dev_get_drvdata(dev))
		dev_set_drvdata(dev, v4l2_dev);

	return 0;
}

video_device为用户空间提供设备节点,提供系统调用的相关操作(open、ioctl…),video_device结构体定义如下:

struct video_device
{
	......

	//应用层open、ioctl等,会调用到fops里的操作函数
	const struct v4l2_file_operations *fops;  

	u32 device_caps;

	/* sysfs */
	struct device dev;

	//字符设备
	struct cdev *cdev;

	struct v4l2_device *v4l2_dev;

	struct device *dev_parent;

	struct v4l2_ctrl_handler *ctrl_handler;

	//v4l2的缓存管理
	struct vb2_queue *queue;

	

	//video device name
	char name[32];

	//v4l2设备类型,对于摄像头,该字段为VFL_TYPE_GRABBER
	int vfl_type;


	//次设备号
	int minor;

	/* 设备编号,同一设备类型的设备编号不能有重复,不同类型的话,设备编号可以相同
   *  比如可以有video0、radio0(不同类型,同编号)
   */
	u16 num;   

	unsigned long flags;

	int index;

	/* V4L2 file handles */
	spinlock_t		fh_lock;
	struct list_head	fh_list;

	......

	const struct v4l2_ioctl_ops *ioctl_ops;
	
	......
};

驱动程序需要设置video_device的ioctl_ops、queue、v4l2_dev、fops等字段,之后调用video_register_device函数进行注册:

/* 参数说明:
 * vdev,要注册的video_device 
 * type,v4l2设备的类型,有VFL_TYPE_GRABBER、VFL_TYPE_VBI、VFL_TYPE_RADIO等
 * nr,为-1表示让系统找一个不冲突的设备编号
 */

static inline int __must_check video_register_device(struct video_device *vdev,
		int type, int nr)
{
	
	return __video_register_device(vdev, type, nr, 1, vdev->fops->owner);
}


int __video_register_device(struct video_device *vdev, int type, int nr,
		int warn_if_nr_in_use, struct module *owner)
{
	int i = 0;
	int ret;
	int minor_offset = 0;
	int minor_cnt = VIDEO_NUM_DEVICES;
	const char *name_base;

	
	vdev->minor = -1;

	......

	/* v4l2_fh support */
	spin_lock_init(&vdev->fh_lock);
	INIT_LIST_HEAD(&vdev->fh_list);

	/* Part 1: check device type */
	switch (type) {
	//摄像头为VFL_TYPE_GRABBER类型的v4l2设备
	case VFL_TYPE_GRABBER:   
		name_base = "video";
		break;
	......
	}

	......

	/* Part 2: find a free minor, device node number and device index. */
	......
	mutex_lock(&videodev_lock);

	//找到一个未使用的设备编号
	nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt); 
	......


#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
......
#else
	for (i = 0; i < VIDEO_NUM_DEVICES; i++)

		//在全局数据video_device里,找到一个空位置,对应位置的i即为次设备号
		if (video_device[i] == NULL)  
			break;
	if (i == VIDEO_NUM_DEVICES) {
		mutex_unlock(&videodev_lock);
		printk(KERN_ERR "could not get a free minor\n");
		return -ENFILE;
	}
#endif

	vdev->minor = i + minor_offset; 
	vdev->num = nr;
	devnode_set(vdev);

	......

	//以次设备为下标,把注册的struct video_device保存在全局数组video_device里
	video_device[vdev->minor] = vdev;
	mutex_unlock(&videodev_lock);

	if (vdev->ioctl_ops)
		determine_valid_ioctls(vdev);

	/* Part 3: 初始化字符设备 */

	//申请字符设备
	vdev->cdev = cdev_alloc(); 
	......

	//设置字符设备的file_operations,为v4l2_fops
	vdev->cdev->ops = &v4l2_fops;  
	vdev->cdev->owner = owner;

	//注册字符设备
	ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); 
	......

	/* Part 4: register the device with sysfs */
	vdev->dev.class = &video_class;
	vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor); //主设备为81
	vdev->dev.parent = vdev->dev_parent;

	//设置设备的名称,如果为VFL_TYPE_GRABBER类型,设备编号为0,则名称为video0
	dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
	ret = device_register(&vdev->dev);  //注册device,生成设备文件
	if (ret < 0) {
		printk(KERN_ERR "%s: device_register failed\n", __func__);
		goto cleanup;
	}
	
	vdev->dev.release = v4l2_device_release;

	......

}


/* drivers/media/v4l2-core/v4l2-dev.c */
#define VIDEO_NUM_DEVICES	256
static struct video_device *video_device[VIDEO_NUM_DEVICES];

注册video_device之后,会在/dev目录下产生设备文件,open、ioctl等操作会调用到v4l2_fops里对应的操作函数:

static const struct file_operations v4l2_fops = {
	.owner = THIS_MODULE,
	.read = v4l2_read,
	.write = v4l2_write,
	.open = v4l2_open,
	.get_unmapped_area = v4l2_get_unmapped_area,
	.mmap = v4l2_mmap,
	.unlocked_ioctl = v4l2_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl = v4l2_compat_ioctl32,
#endif
	.release = v4l2_release,
	.poll = v4l2_poll,
	.llseek = no_llseek,
};

如"open /dev/video0",调用到v4l2_open:

static int v4l2_open(struct inode *inode, struct file *filp)
{
	struct video_device *vdev;
	int ret = 0;

	/* Check if the video device is available */
	mutex_lock(&videodev_lock);

	//以次设备号在video_device数组中找到对应的struct video_device 
	vdev = video_devdata(filp); 
	......


	/* and increase the device refcount */
	video_get(vdev);
	mutex_unlock(&videodev_lock);
	if (vdev->fops->open) {
		if (video_is_registered(vdev))
			//调用struct video_device的fops成员里的open回调
			ret = vdev->fops->open(filp); 
		else
			ret = -ENODEV;
	}

	......
}

同样的,进行ioctl、poll等操作、最终会转接到调用struct video_device的fops成员里的回调函数。struct video_device的fops成员,为struct v4l2_file_operations类型,定义如下:

struct v4l2_file_operations {
	struct module *owner;
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	long (*ioctl) (struct file *, unsigned int, unsigned long);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
#ifdef CONFIG_COMPAT
	long (*compat_ioctl32) (struct file *, unsigned int, unsigned long);
#endif
	unsigned long (*get_unmapped_area) (struct file *, unsigned long,
				unsigned long, unsigned long, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct file *);
	int (*release) (struct file *);
};

struct video_device的fops成员是驱动程序设置的,但fops里的ioctl和poll成员可以不用驱动程序实现,内核提供的有实现好的函数,驱动程序可以这样设置struct video_device的fops成员:

static struct v4l2_file_operations xxx_fops = {
	.owner		= THIS_MODULE,
	......
	.poll		= vb2_fop_poll,          
	.unlocked_ioctl	= video_ioctl2,  
	......
};

应用层操控v4l2设备基本上是通过ioctl,如查询设备功能、设置输入设备、设置图像格式、申请缓存等。
当应用层"ioctl /dev/video0",其调用流程图:
在这里插入图片描述
struct video_device的ioctl_ops成员,类型为struct v4l2_ioctl_ops,定义如下:

struct v4l2_ioctl_ops {
	/* ioctl callbacks */

	/* VIDIOC_QUERYCAP handler */
	int (*vidioc_querycap)(struct file *file, void *fh,
			       struct v4l2_capability *cap);

	/* VIDIOC_ENUM_FMT handlers */
	int (*vidioc_enum_fmt_vid_cap)(struct file *file, void *fh,
				       struct v4l2_fmtdesc *f);
	int (*vidioc_enum_fmt_vid_overlay)(struct file *file, void *fh,
					   struct v4l2_fmtdesc *f);
	int (*vidioc_enum_fmt_vid_out)(struct file *file, void *fh,
				       struct v4l2_fmtdesc *f);
	int (*vidioc_enum_fmt_vid_cap_mplane)(struct file *file, void *fh,
					      struct v4l2_fmtdesc *f);
	int (*vidioc_enum_fmt_vid_out_mplane)(struct file *file, void *fh,
					      struct v4l2_fmtdesc *f);
	......

	/* VIDIOC_G_FMT handlers */
	int (*vidioc_g_fmt_vid_cap)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_g_fmt_vid_overlay)(struct file *file, void *fh,
					struct v4l2_format *f);
	int (*vidioc_g_fmt_vid_out)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_g_fmt_vid_out_overlay)(struct file *file, void *fh,
					    struct v4l2_format *f);
	int (*vidioc_g_fmt_vbi_cap)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_g_fmt_vbi_out)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_g_fmt_sliced_vbi_cap)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_g_fmt_sliced_vbi_out)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_g_fmt_vid_cap_mplane)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_g_fmt_vid_out_mplane)(struct file *file, void *fh,
					   struct v4l2_format *f);
	......


	/* VIDIOC_S_FMT handlers */
	int (*vidioc_s_fmt_vid_cap)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_s_fmt_vid_overlay)(struct file *file, void *fh,
					struct v4l2_format *f);
	int (*vidioc_s_fmt_vid_out)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_s_fmt_vid_out_overlay)(struct file *file, void *fh,
					    struct v4l2_format *f);
	int (*vidioc_s_fmt_vbi_cap)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_s_fmt_vbi_out)(struct file *file, void *fh,
				    struct v4l2_format *f);
	int (*vidioc_s_fmt_sliced_vbi_cap)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_s_fmt_sliced_vbi_out)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_s_fmt_vid_cap_mplane)(struct file *file, void *fh,
					   struct v4l2_format *f);
	int (*vidioc_s_fmt_vid_out_mplane)(struct file *file, void *fh,
					   struct v4l2_format *f);
	......


	/* VIDIOC_TRY_FMT handlers */
	int (*vidioc_try_fmt_vid_cap)(struct file *file, void *fh,
				      struct v4l2_format *f);
	int (*vidioc_try_fmt_vid_overlay)(struct file *file, void *fh,
					  struct v4l2_format *f);
	int (*vidioc_try_fmt_vid_out)(struct file *file, void *fh,
				      struct v4l2_format *f);
	int (*vidioc_try_fmt_vid_out_overlay)(struct file *file, void *fh,
					     struct v4l2_format *f);
	int (*vidioc_try_fmt_vbi_cap)(struct file *file, void *fh,
				      struct v4l2_format *f);
	int (*vidioc_try_fmt_vbi_out)(struct file *file, void *fh,
				      struct v4l2_format *f);
	int (*vidioc_try_fmt_sliced_vbi_cap)(struct file *file, void *fh,
					     struct v4l2_format *f);
	int (*vidioc_try_fmt_sliced_vbi_out)(struct file *file, void *fh,
					     struct v4l2_format *f);
	int (*vidioc_try_fmt_vid_cap_mplane)(struct file *file, void *fh,
					     struct v4l2_format *f);
	int (*vidioc_try_fmt_vid_out_mplane)(struct file *file, void *fh,
					     struct v4l2_format *f);
	......

	/* Buffer handlers */
	int (*vidioc_reqbufs)(struct file *file, void *fh,
			      struct v4l2_requestbuffers *b);
	int (*vidioc_querybuf)(struct file *file, void *fh,
			       struct v4l2_buffer *b);
	int (*vidioc_qbuf)(struct file *file, void *fh,
			   struct v4l2_buffer *b);
	int (*vidioc_expbuf)(struct file *file, void *fh,
			     struct v4l2_exportbuffer *e);
	int (*vidioc_dqbuf)(struct file *file, void *fh,
			    struct v4l2_buffer *b);

	int (*vidioc_create_bufs)(struct file *file, void *fh,
				  struct v4l2_create_buffers *b);
	int (*vidioc_prepare_buf)(struct file *file, void *fh,
				  struct v4l2_buffer *b);

	int (*vidioc_overlay)(struct file *file, void *fh, unsigned int i);
	int (*vidioc_g_fbuf)(struct file *file, void *fh,
			     struct v4l2_framebuffer *a);
	int (*vidioc_s_fbuf)(struct file *file, void *fh,
			     const struct v4l2_framebuffer *a);

		/* Stream on/off */
	int (*vidioc_streamon)(struct file *file, void *fh,
			       enum v4l2_buf_type i);
	int (*vidioc_streamoff)(struct file *file, void *fh,
				enum v4l2_buf_type i);

	/*
	 * Standard handling
	 *
	 * Note: ENUMSTD is handled by videodev.c
	 */
	int (*vidioc_g_std)(struct file *file, void *fh, v4l2_std_id *norm);
	int (*vidioc_s_std)(struct file *file, void *fh, v4l2_std_id norm);
	int (*vidioc_querystd)(struct file *file, void *fh, v4l2_std_id *a);

		/* Input handling */
	int (*vidioc_enum_input)(struct file *file, void *fh,
				 struct v4l2_input *inp);
	int (*vidioc_g_input)(struct file *file, void *fh, unsigned int *i);
	int (*vidioc_s_input)(struct file *file, void *fh, unsigned int i);

		/* Output handling */
	int (*vidioc_enum_output)(struct file *file, void *fh,
				  struct v4l2_output *a);
	int (*vidioc_g_output)(struct file *file, void *fh, unsigned int *i);
	int (*vidioc_s_output)(struct file *file, void *fh, unsigned int i);

	......

	
	/* For other private ioctls */
	long (*vidioc_default)(struct file *file, void *fh,
			       bool valid_prio, unsigned int cmd, void *arg);
};

当然struct video_device的ioctl_ops成员里的各种回调函数不需要全部实现,来看一个具体的例子:

static const struct v4l2_ioctl_ops  vir_v4l2_ioctl_ops = {
	//查询设备功能
	.vidioc_querycap          = vir_vidioc_querycap,

	//枚举设备支持的图像格式
	.vidioc_enum_fmt_vid_cap  = vir_enum_fmt_vid,

	//测试设备是否支持此格式
	.vidioc_try_fmt_vid_cap   = vir_vidioc_try_fmt_vid_cap,

	//获取当前设备的图像格式
	.vidioc_g_fmt_vid_cap     = vir_vidioc_g_fmt_vid_cap,

	//设置图像格式
	.vidioc_s_fmt_vid_cap     = vir_vidioc_s_fmt_vid_cap,

	//申请缓存
	.vidioc_reqbufs       = vir_vidioc_reqbufs,
	
	//获取缓存信息
	.vidioc_querybuf      = vir_vidioc_querybuf,

	//将缓存放入队列中
	.vidioc_qbuf          = vir_vidioc_qbuf,

	//将缓存从队列中取出
	.vidioc_dqbuf         = vir_vidioc_dqbuf,

	//枚举视频输入设备
	.vidioc_enum_input    = vir_vidioc_enum_input,

	//获取当前的视频输入设备
	.vidioc_g_input       = vir_vidioc_g_input,

	//设置视频输入设备
	.vidioc_s_input       = vir_vidioc_s_input,

	//打开设备
	.vidioc_streamon      = vir_vidioc_streamon,

  //关闭设备
	.vidioc_streamoff     = vir_vidioc_streamoff,

}

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

相关文章

MS CRM Callouts Tip - Debug and Development铪铪铪铪

A debug tool for CRM Callouts由于Callouts是在CRM的服务器端由CRM Server调用&#xff0c;并不是一个独立的进程&#xff0c;因此开发人员在pc上调试Callouts将遇到一个挑战&#xff0c;设置断点这种最简单的调试技术都好像成了不可能的任 务。http://www.stunnware.com/crm2…

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

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

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…