韦东山项目视频之摄像头驱动1 V4L2框架分析

一、摄像头驱动 V4L2框架分析

摄像头驱动是属于字符设备驱动程序

V4L2:  vidio for linux version 2,我们分析的是linux3.4.2内核。

回顾二期,怎么写驱动?

1.构造一个file_operations:.open=drv_open .read=drv_read
2.告诉内核:register_chrdev(主设备号,名字,&file_operations)
3.入口函数:调用register_chrdev
4.出口函数:卸载
一般采用register_chrdev的代替方法:分配、设置cdev,cdev_add

而对于复杂的驱动,采用分层的概念。
例如LCD驱动中分为两层:上层通用的核心层内核已经帮我们做好,即在fbmem.c
1.构造file_operations(open read write 。。)
2.注册 

 3.入口、出口

我们做的是硬件相关层,供上层file_operations调用
1.分配一个fb_info 结构体
2.设置
3.注册
4.硬件相关的操作

因此,对于这种复杂的驱动,我们的做法:
1.分配某个结构体
2.设置
3.注册
4.硬件相关

现在分析V4L2框架:

把usb设备接到系统前台,会有打印信息,根据打印信息在内核里找出驱动,用dmsg命令查看;
grep "Found UVC" * -nR 搜索 在uvc_driver.c里,这是个硬件相关的驱动。

分析代码,猜测V4L2  框架 肯定也是分为至少两层  。


app 调用 open read write -->调用 v4l2_fops 里的 open read write->调用硬件相关层的video_device 里提供的函数
----------------------------------------------------------------------------------------------------------
核心层:v4l2-dev.c   __video_register_device  
    构造:v4l2_fops(.read = v4l2_read,
     .write = v4l2_write,
                             .open = v4l2_open, 。。。。)
    注册:
    vdev->cdev = cdev_alloc();     //1.字符设备cdev_alloc
    vdev->cdev->ops = &v4l2_fops;   //2.设置fops
    cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);  //3.cdev_add


----------------------------------------------------------------------------------------------------------
硬件相关层:如uvc_driver.c   Found UVC ->v4l2_device_register(这个不重要)
    ->video_device_alloc->video_register_device(向核心层注册) 
    ->v4l2-dev.h->__video_register_device(v4l2-dev.c)     
    
    即分配结构体  video_device (里面的函数供上层v4l2_fops调用)   
    设置  注册video_register_device            


以vivi.c(virtual video driver )虚拟视频驱动 作为例子
1.分配一个video_device 结构体
2.设置
3.注册 video_register_device     

----------------------------------------------------------------------------------------------------------
硬件相关层


从入口函数开始分析
vivi_init
vivi_create_instance


v4l2_device_register //不是主要的


下面大堆函数是用来设置音量、亮度、增益等属性,用于APP的ioctl
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,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_CONTRAST, 0, 255, 1, 16);


等等...


vfd = video_device_alloc(); //分配video_device 
*vfd = vivi_template; //内容设置为vivi_template
/*最底层的vivi 操作函数*/
static struct video_device vivi_template = {
.name = "vivi",
.fops           = &vivi_fops,
.ioctl_ops = &vivi_ioctl_ops,
.release = video_device_release,
.tvnorms              = V4L2_STD_525_60,
.current_norm         = V4L2_STD_NTSC_M,
};
vfd->v4l2_dev = &dev->v4l2_dev;


video_register_device(video_device结构体vfd, 类型VFL_TYPE_GRABBER, video_nr); //向上注册
----------------------------------------------------------------------------------------------------------
核心层
__video_register_device
根据类型VFL_TYPE_GRABBER创建不同的设备节点
case VFL_TYPE_GRABBER:
name_base = "video";
根据类型VFL_TYPE_GRABBER得到不同的次设备号
case VFL_TYPE_GRABBER:
minor_offset = 0;
minor_cnt = 64;
vdev->cdev = cdev_alloc(); 
vdev->cdev->ops = &v4l2_fops;
cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);     //3.cdev_add


video_device[vdev->minor] = vdev;//以次设备号为下标在数组里面得到一项把vdev存进来



分析vivi.c 的open read write ioctl 过程 -
1.open过程 -
APP  :open (“/dev/video0”...) -
-
drv  : v4l2_fops里的v4l2_open函数 -
vdev = video_devdata(filp)//根据次设备号从数组中得到video_device---------
if (vdev->fops->open)     //如果有open函数
if (video_is_registered(vdev))
ret = vdev->fops->open(filp);//调用open 函数
调用vivi.c 里的v4l2_fh_open


2.read过程
APP  :read (“/dev/video0”...)


drv  :v4l2_fops里的v4l2_read 函数
struct video_device *vdev = video_devdata(filp);//根据次设备号从数组中得到video_device
if (video_is_registered(vdev))
       ret = vdev->fops->read(filp, buf, sz, off);//调用read 函数
调用vivi.c 里的vivi_read


2.ioctl 过程(比较复杂)
APP  :ioctl(“/dev/video0”...)


drv  : v4l2_fops里的v4l2_ioctl函数
struct video_device *vdev = video_devdata(filp);//根据次设备号从数组中得到video_device
if (vdev->fops->unlocked_ioctl)
ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);//调用v4l2_ioctl函数
调用vivi.c 里的video_ioctl2

这个video_ioctl2做什么呢?
video_usercopy(file, cmd, arg, __video_do_ioctl); 
video_usercopy:从用户空间把用户的命令复制进来,调用__video_do_ioctl
__video_do_ioctl
struct video_device *vfd = video_devdata(file);//根据次设备号从数组中得到video_device
根据用户空间APP得到的命令(cmd) 设置某些属性
switch (cmd) 


怎么设置这些属性?还得由vivi.c来提供,在vivi.c 里一开始的vivi_create_instance里设置


v4l2_ctrl_handler的使用过程
分析__video_do_ioctl


二、怎么写v4l2驱动?
1.分配、设置、注册v4l2_device  v4l2_device_register video_register_device
2.分配一个video_device video_device_alloc
.vfd->v4l2_dev
.fops           设置vfd的fops 里的open、read、write 被上层调用
.ioctl_ops      设置属性被上层调用    

思考:APP可以通过ioctl来设置(获得)亮度等信息,在驱动程序里,谁来接收、存储、设置到硬件(提供这些信息)?
答:在驱动程序中抽象出来一个结构体v4l2_ctrl,每个Ctrl对应其中的一项(音量、亮度等等);
    由v4l2_ctrl_handler来管理他们
  1.初始化
    v4l2_ctrl_handler_init 
  2.设置
    v4l2_ctrl_new_std
    v4l2_ctrl_new_custom
    这些函数就是创建各个属性,并且放入v4l2_ctrl_handler的链表
  3.跟vdev关联
    dev->v4l2_dev.ctrl_handler = hdl;













你可能感兴趣的:(Linux摄像头)