LINUX摄像驱动一:V4L2大致框架及vivi初步分析

来源:互联网 发布:淘宝优惠券哪里领取 编辑:程序博客网 时间:2024/05/22 14:48

回顾:
怎么写分层驱动:
1、分配某结构体
2、设置
3、注册
4、硬件相关操作

V4L2框架可参考文章:
http://blog.csdn.net/lizuobin2/article/details/53000720
http://blog.csdn.net/lanbing510/article/details/24204655?locationNum=15&fps=1

这里写图片描述

一. V4L2框架: video for linux version 2
大致猜测:
至少分为两层:
第一层:核心层: v4l2_dev.c
1、编写file_operations结构体
2、注册
然后:
cdev_alloc
cdev->ops = v4l2_fops
cdev_add
第二层:硬件相关层:
1、分配video_device
2、设置
3、注册vido_register_device
uvc_driver.c
v4l2_device_register 这个其实并不重要
video_device_alloc
video_register_device 这个重要

虚拟视频驱动vivi.c分析:
1.分配 video_device
2.设置
3.注册:video_register_device

ViVi分析:

vivi_init    vivi_create_instance        v4l2_device_register   // 不是主要, 只是用于初始化一些东西,比如自旋锁、引用计数 、并没有注册什么东西        video_device_alloc     //分配vido_device设备,分配好后进行设置        // 设置          1. vfd:            .fops           = &vivi_fops,     //Garmen:这个fops 指的是 :v4l2_file_oprations结构体            .ioctl_ops     = &vivi_ioctl_ops,            .release    = video_device_release,          2.            vfd->v4l2_dev = &dev->v4l2_dev;     //Garmen:dev->v4l2_dev这个结构体,实际上就是前面v4l2_device_register函数得到的结构体!          3. 设置"ctrl属性"(用于APP的ioctl):                v4l2_ctrl_handler_init(hdl, 11);                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);               //亮度     0:亮度Min,255:亮度Max,127:默认                dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,                        V4L2_CID_CONTRAST, 0, 255, 1, 16);                Garmen:进行好上面的分配之后,然后进行注册!        video_register_device(video_device, type:VFL_TYPE_GRABBER, nr)     //Garmen: 根据这个类型type:VFL_TYPE_GRABBER,创建不同的设备节点;不同类型,次设备号不同            __video_register_device                vdev->cdev = cdev_alloc();          //Garmen: 从这里我们可以看到,不管驱动框架多么复杂,最后还是有cdev_alloc();和cdev_add;                vdev->cdev->ops = &v4l2_fops;          //猜测:v4l2_fops 可能就是上面第1点里面的.fops                cdev_add                video_device[vdev->minor] = vdev;     //Garmen:以次设备号为下标,将vdev结构体存入video_device,当我们调用open函数的时候就反方向操作               Garmen:对应了问1里面的问题;                if (vdev->ctrl_handler == NULL)                    vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;

附上vivi.c 中vivi_init里的vivi_create_instance函数(这是主要函数)

static int __init vivi_create_instance(int inst){    struct vivi_dev *dev;    struct video_device *vfd;    struct v4l2_ctrl_handler *hdl;    struct vb2_queue *q;    int ret;    dev = kzalloc(sizeof(*dev), GFP_KERNEL);    if (!dev)        return -ENOMEM;    snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),            "%s-%03d", VIVI_MODULE_NAME, inst);    ret = v4l2_device_register(NULL, &dev->v4l2_dev);    if (ret)        goto free_dev;    dev->fmt = &formats[0];    dev->width = 640;    dev->height = 480;    hdl = &dev->ctrl_handler;    v4l2_ctrl_handler_init(hdl, 11);    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);    dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,            V4L2_CID_SATURATION, 0, 255, 1, 127);    dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,            V4L2_CID_HUE, -128, 127, 1, 0);    dev->autogain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,            V4L2_CID_AUTOGAIN, 0, 1, 1, 1);    dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,            V4L2_CID_GAIN, 0, 255, 1, 100);    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);    dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL);    dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);    dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL);    dev->bitmask = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_bitmask, NULL);    if (hdl->error) {        ret = hdl->error;        goto unreg_dev;    }    v4l2_ctrl_auto_cluster(2, &dev->autogain, 0, true);    dev->v4l2_dev.ctrl_handler = hdl;    /* initialize locks */    spin_lock_init(&dev->slock);    /* initialize queue */    q = &dev->vb_vidq;    memset(q, 0, sizeof(dev->vb_vidq));    q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;    q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ;    q->drv_priv = dev;    q->buf_struct_size = sizeof(struct vivi_buffer);    q->ops = &vivi_video_qops;    q->mem_ops = &vb2_vmalloc_memops;    vb2_queue_init(q);    mutex_init(&dev->mutex);    /* init video dma queues */    INIT_LIST_HEAD(&dev->vidq.active);    init_waitqueue_head(&dev->vidq.wq);    ret = -ENOMEM;    vfd = video_device_alloc();    if (!vfd)        goto unreg_dev;    *vfd = vivi_template;    vfd->debug = debug;    vfd->v4l2_dev = &dev->v4l2_dev;    set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);    /*     * Provide a mutex to v4l2 core. It will be used to protect     * all fops and v4l2 ioctls.     */    vfd->lock = &dev->mutex;    ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);    if (ret < 0)        goto rel_vdev;    video_set_drvdata(vfd, dev);    /* Now that everything is fine, let's add it to device list */    list_add_tail(&dev->vivi_devlist, &vivi_devlist);    if (video_nr != -1)        video_nr++;    dev->vfd = vfd;    v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",          video_device_node_name(vfd));    return 0;rel_vdev:    video_device_release(vfd);unreg_dev:    v4l2_ctrl_handler_free(hdl);    v4l2_device_unregister(&dev->v4l2_dev);free_dev:    kfree(dev);    return ret;}

对于一个驱动程序,你要想彻底的弄清楚它,就是要去分析它的open,read,write,ioctl过程
分析vivi.c的open,read,write,ioctl过程

1. openapp:     open("/dev/video0",....)---------------------------------------------------drv:     v4l2_fops.v4l2_open            vdev = video_devdata(filp);  // 根据次设备号从数组中得到video_device            ret = vdev->fops->open(filp);                        vivi_ioctl_ops.open    //转到了我们硬件相关层所提供的open函数了                            v4l2_fh_open2. readapp:    read ....---------------------------------------------------drv:    v4l2_fops.v4l2_read          //调用file_operations v4l2_fops            struct video_device *vdev = video_devdata(filp);    //首先根据次设备号,从数组里面得到之前注册的video_device结构体            ret = vdev->fops->read(filp, buf, sz, off);            //然后调用它的fops里的read函数;3. ioctlapp:   ioctl----------------------------------------------------drv:   v4l2_fops.unlocked_ioctl            v4l2_ioctl                struct video_device *vdev = video_devdata(filp);                ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);                            video_ioctl2          //分析了vivi_fops中的ioctl函数;                                video_usercopy(file, cmd, arg, __video_do_ioctl);                                         __video_do_ioctl                                        struct video_device *vfd = video_devdata(file);                                        根据APP传入的cmd来获得、设置"某些属性"                                        //Garmen:发现大概是做了这些事情:从用户空间,根据这些参数,把用户空间的参数复制进来,                                        然后调用 __video_do_ioctl这个参数----------------------------------------------------4、v4l2_ctrl_handler的使用过程:    __video_do_ioctl        struct video_device *vfd = video_devdata(file);        case VIDIOC_QUERYCTRL:        {            struct v4l2_queryctrl *p = arg;            if (vfh && vfh->ctrl_handler)                ret = v4l2_queryctrl(vfh->ctrl_handler, p);            else if (vfd->ctrl_handler)  // 问1:在哪设置?   答1:在video_register_device                ret = v4l2_queryctrl(vfd->ctrl_handler, p);                            // 根据ID在ctrl_handler里找到v4l2_ctrl,返回它的值                               Garmen:我们在vivi.c里面,所有创建的v4l2_ctrl,都有一个ID!

Garmen:小结
怎么写V4L2驱动:
实际上并没有脱离字符设备驱动程序的框架!
1、分配、设置、注册 V4L2_device (实际上并不重要,但是它里面提供了一些辅助信息,比如自旋锁、引用计数 )
它的关键函数是:v4l2_device_register,得到一个结构体v4l2_device
2、分配:vido_device,调用vido_device_alloc函数
3、设置:
A:vfd,它里面有一些结构体,比如v4l2_dev,我们将它指向V4L2_device,我们以后会引用到里面的一些结构体
B : vfd里面还有很多结构体,例如vfd.fops,fops里面有open、read、write函数,被上层的V4l2_fops调用
ioctl_ops 调用
C : App可以通过ioctl来设置亮度等信息,驱动程序里,谁来接收/存储/设置到硬件
App可以通过ioctl来获得亮度等信息,驱动程序里,谁来提供这些信息呢?
我们用V4l2_ctrl
问:谁来管理?
答:V4l2_ctrl_handle
该函数主要要完成的事情
①:初始化V4l2_ctrl_handler_init,进行V4l2_ctrl_handle初始化
②:设置;添加了类似V4l2_Ctrl_new_std、V4l2_Ctrl_new_custom的各项函数
问:这些函数干嘛用呢?
答:创建各个V4l2_Ctrl,并且放入V4l2_ctrl_handle这个链表
③:跟vdev进行关联
v4l2_dev.ctrl_handler = hdl;