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

回顾:
怎么写分层驱动:
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. open
app: 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_open

2. read
app: 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. ioctl
app: 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;

你可能感兴趣的:(linux)