第一章 UVC驱动之虚拟视频驱动

第一章 UVC驱动之虚拟视频驱动


1.1视频驱动的整体框架
内核的V4L2 摄像头驱动的设计采用分离分层的思想,整个框架分为四大部分:应用空间、内核核心层、硬件驱动相关层;具体硬件设备。


视频驱动的整体框架见下图:
 
图1.1 V4L2驱动框架
Figure 1.1 V4L2 driver framework


1.2视频驱动代码流程分析
1.2.1 虚拟视频驱动分析myuvc.c
Linux内核版本:        Linux-3.4.2
虚拟视频驱动代码路径: Linux-3.4.2/drivers/media/video/myuvc.c
myuvc驱动涉及文件:
myuvc.c                        驱动的具体实现
v4l2-common.c
v4L2-dev.c    video_register_device(struct video_device *vdev...);
v4L2-device.c v4l2_device_register(struct device *dev,struct v4l2_device *v4l2_device);
videobuf_core.c
videobuf_vmalloc.c

分析一个驱动程序最快捷的方法就是从驱动的入口(模块加载函数)函数开始,在驱动的入口函数里大部分就是分配、设置和注册该驱动模型的核心结构体,虚拟视频驱动程序也不例外。
1.2.1.1入口函数:
static int __init myuvc_init(void)
{
……
ret = myuvc_create_instance(i); /*创建设备*/
……
}
此入口清晰明了,直接封装了一个函数来调用myuvc_create_instance(i);这个函数才是重点内容。分析此函数要抓住一下三大要点:
1.分配video_device
2.设置这个结构体
3.注册:video_register_device




1.2.1.2分析myuvc_create_instance(i)函数:
static int __init myuvc_create_instance(int inst)
{
……
/* 1:只是用于初始化一些东西,比如自旋锁、引用计数 */
ret = v4l2_device_register(NULL, &dev->v4l2_dev);
……
/* 添加一个新标准的cortl */
dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
/* 设置亮度属性 最大值255; 最小值0;默认值127 */
dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
……
/* 添加一个客户自定义的cortl */
dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL);
dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL);
……
/* 2:分配video_device 设备 */
vfd = video_device_alloc();


/*:3:设置video_device  
/* 设置video_device  
 * vfd = myuvc_template;等价于:
 * vfd->fops    = &myuvc_fops,  --->v4l2_file_operations
 * vfd->ioctl_ops      = &myuvc_ioctl_ops, ---> v4l2_ioctl_ops
 */
*vfd = myuvc_template;
vfd->debug = debug;
/*来自ret = v4l2_device_register(NULL, &dev->v4l2_dev);*/
vfd->v4l2_dev = &dev->v4l2_dev;


……


/* 注册video_device 
 * 根据类型VFL_TYPE_GRABBER 自动创建不同的设备节点
 */
ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); ……
}
myuvc_create_instance函数的框架其是也很简单明了,就是围绕了三个步骤:分配、设置、注册核心结构体。
在此函数当中,重点在于对video_device结构体的设置,现在就来重点分析它到底做了哪些设置?下面就来看看
在分配video_device 设备vfd = video_device_alloc();之后,把vfd的内容设置为vfd = myuvc_template;。而myuvc_template实例的内容如下图所示:


 
此对象实体有myuvc_fops和myuvc_ioctl_ops,分别是v4l2_file_operations和v4l2_ioctl_ops结构体的实体。
myuvc_fops内容如下:
     myuvc_ioctl_ops内容如下:
 
从以上的myuvc_fops和myuvc_ioctl_ops内容知道了,此次时把文件操作函数集和驱动的控制接口绑定到了video_device的fops和ioctl_ops成员当中,以后应用程序调用open、read、write、ioctl等一系列标准函数时,最终将会调用到这里绑定的两个函数操作集合。


接下来我们就仔细的分析video_register_device这个注册函数,看看此函数到底做了些什么工作?
1.2.1.3分析video_register_device函数:
由于此函数涉及到大量的代码调用流程,因此下面直接利用代码的调用关系进行分析:
video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
__video_register_device(vdev, type, nr, 1, vdev->fops->owner);
   各种参数判断
/* 根据类型得到设备的名字 
*   比如video0 video1 .......
*/
switch (type) {
case VFL_TYPE_GRABBER:
name_base = "video";
break;
……
}
/* 也根据不同的类型设置次设备号 */
switch (type) {
case VFL_TYPE_GRABBER:
minor_offset = 0;
minor_cnt = 64;
break;
……
}
……
/* 分配字符设备核心结构 */
vdev->cdev = cdev_alloc();
/* 设置字符设备的file_operations */
vdev->cdev->ops = &v4l2_fops;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
/* 注册字符设备 */
ret = device_register(&vdev->dev);
/* 以次设备号为数组下标,
* 把video_device 存入video_device数组 
*/
video_device[vdev->minor] = vdev; video_register_device函数的框架也很简单了,核心还是分配、设置、注册字符设备驱动核心结构体,这个函数位于核心层v4l2_dev.c里面。在此函数里面也要设置一个核心结构体并且注意到此次也有一个file_operations v4l2_fops 文件操作函数集,而在上一层的myuvc_create_instance函数里面也有一个v4l2_file_operations myuvc_fops文件操作函数集。那么这两个文件操作函数集合到底有什么联系?关系就是核心层的v4l2_fops文件操作函数集将会通过某种方式去调用到驱动相关层的myuvc_fops文件操作函数集。下面就以open、ioctl等函数为例子解析。


1:open过程
假设应用程序去open一个摄像头设备“/dev/videoX”得到一个文件句柄,了解过Linux系统编程的人就会明白,此文件句柄里面会有文件设备的主设备号、次设备号等等信息。此时,通过次设备号最终会导致驱动核心层的open函数被调用,也就是v4l2_fops.open : v4l2_open函数。


App:                fd=open("/dev/video0",....)
Drv: v4l2_fops.open
即:static int v4l2_open(struct inode *inode, struct file *filp)
/* 定义一个video_device 结构体 */
struct video_device *vdev;
/* 根据次设备号从数组中得到video_device 
* video_device 是在核心层的video_register_device
* 过程以次设备号为小标存入video_device数组
*/
vdev = video_devdata(filp);
……
/* 如果 video_device 里面的video_device. fops有提供
*  open函数就调用它的open函数也就是
*  v4l2_fh_open函数,此时完成了核心层
*  到硬件驱动层
*/
if (vdev->fops->open) 














2. ioctl
app:   ioctl
drv:   v4l2_fops.unlocked_ioctl
         v4l2_ioctl
/* 根据次设备号从数组中得到video_device 
* video_device 是在核心层的video_register_device
* 过程以次设备号为小标存入video_device数组
*/
            struct video_device *vdev = video_devdata(filp);
     /* 如果 video_device 里面的video_device. fops有提供
  * unlocked_ioctl 就调用它的unlocked_ioctl即video_ioctl2
  */
                ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
                  video_ioctl2
 /* 根据应用空间传递来的参数
  * 去调用__video_do_ioctl 函数
  */
                      video_usercopy(file, cmd, arg, __video_do_ioctl);
                         __video_do_ioctl
  // 根据次设备号从数组中得到video_device
                           struct video_device *vfd = video_devdata(file);
                           根据APP传入的cmd来获得、设置"某些属性" case VIDIOC_QUERYCAP:   capabilities
{ ……  }
case VIDIOC_G_PRIORITY: 优先级
{……}
……


问题:这些cmd在哪里添加设置进去的?
答:在驱动相关代码的vivi_create_instance函数当中:
//设置cotrl属性用于app的ioctrl
/* 添加一个新标准的iocotrl */
dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);
dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
/* 设置亮度属性 最大值255; 最小值0;默认值127 */
V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_CONTRAST, 0, 255, 1, 16);
……
/* 添加一个客户自定义的cortl */
dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL);
dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL);
dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

奔跑吧撸码兄弟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值