V4L2框架分析

V4L2框架分析

NO_1. 术语讲解
	V4L2 即: video for Linux version 2
	uvc: usb video class 
 
NO_2. 硬件相关的
	uvc_driver.c
		static int __init uvc_init(void)
			ret = usb_register(&uvc_driver.driver);
				struct uvc_driver uvc_driver = {
					.driver = {
						.name		= "uvcvideo",
						.probe		= uvc_probe,
						.disconnect	= uvc_disconnect,
						.suspend	= uvc_suspend,
						.resume		= uvc_resume,
						.reset_resume	= uvc_reset_resume,
						.id_table	= uvc_ids,
						.supports_autosuspend = 1,
					},
				};
					static int uvc_probe(struct usb_interface *intf,const struct usb_device_id *id)
						v4l2_device_register(&intf->dev, &dev->vdev);//这个不重要
						uvc_register_chains(dev);
							ret = uvc_register_terms(dev, chain);
								ret = uvc_register_video(dev, stream);
									/* Register the device with V4L. */
									vdev = video_device_alloc();
									vdev->fops = &uvc_fops;
									ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);

	总结1:
	struct video_device *vdev;
	vdev = video_device_alloc();// 1. 分配一个 video_device 结构体
	
	vdev->fops = &uvc_fops;// 2. 设置 fops 相关操作接口
	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);// 3. 注册;
	
	总结2:
	1.0 通过 video_register_device 函数的搜索可知
		video_register_device --> __video_register_device 最终调用的是 v4l2-dev.c 的 __video_register_device 函数,
		可见,v4l2-dev.c 是核心层函数;
		
	2.0 
		int __video_register_device(struct video_device *vdev, int type, int nr,int warn_if_nr_in_use, struct module *owner)
			vdev->cdev = cdev_alloc();// 1. 分配字符型设备结构体
			vdev->cdev->ops = &v4l2_fops;// 2. 设置一个 v4l2_fops 结构体接口,APP 调用 open/read/write/inctrl时就会调用这里的接口函数;
			ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);// 3. 添加进内核,也就是注册;


NO_3. 虚拟视频驱动 vivid.core.c 分析;
	1. 分配一个 video_device 结构体;
	
	2. 设置这个结构体;
	
	3. 注册;

	vivid_init
		ret = vivid_create_instance(i);
			ret = v4l2_device_register(NULL, &dev->v4l2_dev);//这个不是主要的,跟入代码可以看出,这是用于初始化一些东西,比如自旋锁,引用计数等;
			struct video_device *vfd;
			struct vivid_dev *dev;
			dev = kzalloc(sizeof(*dev), GFP_KERNEL);
			/*  1.0 分配一个结构体,这里用 kzalloc 分配一个 vivid_dev 结构体,并让 vfd 指向 dev->vid_cap_dev 
			 *	而不是用 video_device_alloc() 分配,通过查看 video_device_alloc() 
			 *  本质也是调用 kzalloc 分配结构体的
			 */
			vfd = &dev->vid_cap_dev;   
			
			/* 2.0 设置  关键是 vivid_fops 结构体 */
			1.
				strlcpy(vfd->name, "vivid-vid-cap", sizeof(vfd->name));
				vfd->fops = &vivid_fops; // v4l2_file_operations
				vfd->ioctl_ops = &vivid_ioctl_ops;
				vfd->release = video_device_release_empty;
				vfd->v4l2_dev = &dev->v4l2_dev;
				vfd->queue = &dev->vb_vid_cap_q;
				vfd->tvnorms = tvnorms_cap;
			
			2. 
				vfd->v4l2_dev = &dev->v4l2_dev;
				
			3.	设置 "ctrl属性" (用于APP的 ioctrl )
				/* create all controls */
				ret = vivid_create_controls(dev, ccs_cap == -1, ccs_out == -1, no_error_inj,
						in_type_counter[TV] || in_type_counter[SVID] ||
						out_type_counter[SVID],
						in_type_counter[HDMI] || out_type_counter[HDMI]);
					/* User Controls */
					dev->brightness = v4l2_ctrl_new_std(hdl_user_vid, &vivid_user_vid_ctrl_ops,
						V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
			
			/* 3.0 注册 */
			ret = video_register_device(vfd, VFL_TYPE_GRABBER, vid_cap_nr[inst]);
				__video_register_device(struct video_device *vdev, int type, int nr,int warn_if_nr_in_use, struct module *owner)
					/* 在该注册函数里也是按照注册设备驱动的三步骤 */
					vdev->cdev = cdev_alloc();// 1. 分配
					vdev->cdev->ops = &v4l2_fops;// 2. 设置接口 fops 
					ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); // 3. 注册
					
				video_device[vdev->minor] = vdev;// 以次设备号为下标把数组存到 vdev 里;
NO_4. vivid.core.c 的 open,read,write,ioctl 过程;		
	1. open 
		app: open("/dev/videox",....);
		.................................................
		drv: vdev->cdev->ops = &v4l2_fops;--> .open = v4l2_open,
				struct video_device *vdev;
				vdev = video_devdata(filp);// 可见这是根据次设备号从数组中得到 video_device ;
					return video_device[iminor(file_inode(file))];
				
				ret = vdev->fops->open(filp);// 即 vfd->fops = &vivid_fops 的 open ,即 v4l2_fh_open;		
	2. read
		app: read....
		..................................................
		drv: vdev->cdev->ops = &v4l2_fops;--> .read = v4l2_read,
				struct video_device *vdev = video_devdata(filp);
					return video_device[iminor(file_inode(file))];// 可见这是根据次设备号从数组中得到 video_device ;
				ret = vdev->fops->read(filp, buf, sz, off);	
	
	3. write
		类似:通过 vdev->cdev->ops = &v4l2_fops;--> .write 从而调用 vivid-core.c 驱动的 vivid_fops -> v4l2_fh_write;
	
	4. ioctl
		app:ioctl....
		...................................................
		drv:vdev->cdev->ops = &v4l2_fops;--> .unlocked_ioctl = v4l2_ioctl,
				struct video_device *vdev = video_devdata(filp);//也是根据次设备号得到 video_device 结构体;
				ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);即调用 vivid-core.c 的 vivid_fops -> .unlocked_ioctl = video_ioctl2,
					video_ioctl2
						return video_usercopy(file, cmd, arg, __video_do_ioctl);//从用户空间根据cmd,然后调用 __video_do_ioctl
							__video_do_ioctl
								struct video_device *vfd = video_devdata(file);//也是根据次设备号得到 video_device 结构体;
									if (v4l2_is_known_ioctl(cmd)) {
										info = &v4l2_ioctls[_IOC_NR(cmd)];
									}
									/*
									 *	static struct v4l2_ioctl_info v4l2_ioctls[] = {
										 IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
										 .....
										};
										static int v4l_querycap(const struct v4l2_ioctl_ops *ops,struct file *file, void *fh, void *arg)
									 *
									 * 可见,这是根据 APP 传入的 cmd 来获得、设置"某些属性"
									 */
	v4l2_ctrl_handler 的使用过程;
	app --> drv: __video_do_ioctl
		struct video_device *vfd = video_devdata(file);//也是根据次设备号得到 video_device 结构体;
		
NO_4. 如何写 V4L2 驱动?
	1. 分配/设置/注册 v4l2_device 
		通过 v4l2_device_register 注册得到一个 v4l2_dev 结构体:
		这个不是主要的,跟入代码可以看出,这是用于初始化一些东西,比如自旋锁,引用计数等;辅助作用而已;
	
	2. 	分配 video_device ,用 video_device_alloc 函数分配,最终调用的是 kzalloc ;vivid是通过以下方式来分配的;
		struct video_device *vfd;
		struct vivid_dev *dev;
		dev = kzalloc(sizeof(*dev), GFP_KERNEL);
		/*  1.0 分配一个结构体,这里用 kzalloc 分配一个 vivid_dev 结构体,并让 vfd 指向 dev->vid_cap_dev 
		 *	而不是用 video_device_alloc() 分配,通过查看 video_device_alloc() 
		 *  本质也是调用 kzalloc 分配结构体的
		 */
		vfd = &dev->vid_cap_dev;
		
	3. 设置;
		3.1 vfd->v4l2_dev = &dev->v4l2_dev;//调用之前用 v4l2_device_register 得到返回的 v4l2_dev 结构体;
		3.2 vfd->fops = &vivid_fops; // v4l2_file_operations /* vivid_fops 这个结构体接口被核心层的 v4l2_fops 调用 */
		3.3 vfd->ioctl_ops = &vivid_ioctl_ops;//也是调用 v4l2_fops 中的 ioctl ???
		3.4 APP 可以通过 ioctl 来设置亮度等信息,那么驱动程序里,谁来接收/存储/设置到硬件?
			APP 可以通过 ioctl 来获得亮度等信息,那么驱动程序里,谁来提供这些信息?
			属性: v4l2_ctrl
			管理: v4l2_ctrl_handler:
			a. v4l2_ctrl_handler_init //初始化一个 v4l2_ctrl_hander
			b. 用 v4l2_ctrl_new_std and v4l2_ctrl_new_custom 填充 v4l2_ctrl_hander 的属性;
				即创建 v4l2_ctrl 并放入链表 v4l2_ctrl_hander(查看该结构体可知这是一个链表);
			c.如何使用呢?
2. 怎么写 v4l2 的驱动:以 vivid 为参考例子
	a. 分配 video_device 结构体 : 
		my_vivid_device = video_device_alloc();
	b. 设置 
		my_vivid_device->release = my_vivid_reless;
		my_vivid_device->fops = &my_vivid_fops;
		my_vivid_device->ioctl_ops = &my_vivid_ioctl_ops;
			填充 static const struct v4l2_file_operations my_vivid_fops = 
				{
					.unlocked_ioctl = video_ioctl2,
				}static const struct v4l2_ioctl_ops my_vivid_ioctl_ops = 
				{		
					.owner = THIS_MODULE,
						/* 表示他是一个摄像头设备 */
					.vidioc_querycap		= my_vivid_vidioc_querycap,
				
					/* 用于列举、获得、测试、设置摄像头的数据格式 */
					.vidioc_enum_fmt_vid_cap	= my_vivid_vidioc_enum_fmt_vid,
					.vidioc_g_fmt_vid_cap		= my_vivid_vidioc_g_fmt_vid_cap,
					.vidioc_try_fmt_vid_cap 	= my_vivid_vidioc_try_fmt_vid_cap,
					.vidioc_s_fmt_vid_cap		= my_vivid_vidioc_s_fmt_vid_cap,
				
					/* 缓存区操作:申请/查询/放入队列/取出队列 */
					.vidioc_reqbufs                 = vb2_ioctl_reqbufs,
					.vidioc_querybuf                = vb2_ioctl_querybuf,
					.vidioc_qbuf                    = vb2_ioctl_qbuf,
					.vidioc_dqbuf 
				
				};
	c. 注册
		video_register_device(my_vivid_device, VFL_TYPE_GRABBER, -1);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

人在路上……

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

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

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

打赏作者

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

抵扣说明:

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

余额充值