最近打算翻翻linux-2.6.32内核中V4L2的源码,linux-2.6.32.2/Documentation/Video4linux/v4l2-framework.txt有关于V4L2驱动的结构说明,V4L2感觉挺复杂的,所以打算从vivi虚拟驱动入手掌握V4L2驱动

        vivi驱动涉及文件:

            vivi.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

        如果你单独在make menuconfig中将vivi驱动编译为模块时,你需要依次加入这些模块:insmod videobuf_core.ko,insmod videobuf_vmalloc.ko,insmod video_common.ko,insmod vivi.ko等。


       主要关注以下几个结构体:

             struct video_device               视频类设备的基类,另外字符设备驱动的基类为cdev,所有的操作都是围绕cdev结构体

                       struct v4l2_file_operations   包含vivi_read,vivi_open等函数具体实现。在系统调用的时候,会首先调用cdev的file_operations的v4l2_read,v4l2_open等函数,在这些函数中会使用struct file中的video_device来调用vivi_read等函数具体去实现。

                       struct v4l2_ioctl_ops            ioctl操作的函数的具体实现

             struct V4l2_device

             struct videobuf_buffer           视频数据缓冲区,对应一帧视频,里面包含视频的大小等信息

             struct videobuf_queue          视频缓冲区队列

        以上几个结构体可能不会被单独使用,一般都被包含在某个更大的对象结构中,比如video_device和v4l2_device都包含在vivi_dev中,而vivi_dev被包含在viv_fh中,videobuf_buffer被包含在vivi_buffer中。


        下面有必要看看struct vivi_fh和struct vivi_dev和vivi_buffer结构体

        struct vivi_fh{

            struct vivi_dev                *dev;

            struct vivi_fmt                 *fmt;

            unsigned int                    width,height;

            structvideo_buf_queue vb_vidq;

            enum v4l2_buf_type      type;

            unsigned                         charbars[8][3];//用于保存8个色块的RGB值或YUV值

            int                                     input;//控制选择色块彩条的标准

       }

       struct vivi_dev{

            struct list_head          vivi_devlist;//内核双向链表

            struct v4l2_device     v4l2_dev;

            spinlock_t                  slock;

            struct mutex               mutex;

            int                               users;

           struct video_device   *vfd;

           struct vivi_damqueue vidq;//DMA队列

           int                                 h,m,s,ms;//定时器定义

           unsigned long            jiffies;

           char                             timestr[13];

           int                                mv_count;//控制彩条移动

           int                               input;

           int                                qctrl_regs[ARRAY_SIZE(vivi_qctrl)];

      }

      struct vivi_buffer{

          struct videobuf_buffer  vb;

          struct vivi_fmt               *fmt;

      }



下面开始分析程序流程:

在vivi.c文件中

vivi_create_instance()

      v4l2_device_register()

      给video_device vdev对象赋值v4l2_file_operations和v4l2_ioctl_ops等

      video_device_register()

          初始化video_device对象vdev

          初始化video_device中的cdev结构体

                      cdev_add()注册字符设备

          初始化video_device中struct device结构体

                     device_register()在/dev/目录下创建设备节点


struct v4l2_file_operations vivi_fops={

      .owner = THIS_MODULE,

      .open = vivi_open,

      .release = vivi_close,

      .read = vivi_read,

      .poll = vivi_poll,

      .ioctl = video_ioctl2,

      .mmap = vivi_mmap,

};

需要说明的是整个vivi_fops是属于video_device的,这里的vivi_open,vivi_read都是最底层的具体实现,被其他上层的调用。在V4l2_dev.c文件中

struct file_operations v4l2_fops = {

      .owner = THIS_MODULE,

      .read = v4l2_read,

      .write = v4l2_write,

      .open = v4l2_open,

      .mmap = v4l2_mmap,

      .ioctl = v4l2_ioctl,

      ...

}

这个file_opertions v4l2_fops是字符设备给上层提供的系统调用,这里的v4l2_open函数最终调用的还是上面的vivi_open的实现,来看看v4l2_open的实现

static int v4l2_open(struct inode *inode, struct file *filp)
{
    struct video_device *vdev;
    vdev = video_devdata(filp);
    if (vdev->fops->open)
        ret = vdev->fops->open(filp);
}


另外一个很重要的ioctl呢?

在file_operations中的v4l2_ioctl调用的是

static int v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct video_device *vdev = video_devdata(filp);
    return vdev->fops->ioctl(filp, cmd, arg);
}

v4l2_dev.c文件里file_operations中的v4l2_ioctl最终调用的还是vivi.c文件里v4l2_file_operations中的video_ioctl2函数,但是这里的video_ioctl2不像vivi_open,vivi_read等是具体的实现,video_ioctl2具体的实现已经单独拿出来放到v4l2_ioctl.c文件中了,所以video_ioctl2最终调用的是v4l2_ioctl.c文件中的__video_do_ioctl(file,cmd,arg)函数,而__video_do_ioctl(file,cmd,arg)函数又回过来调用vivi_ioctl中vivi_ioctl_ops来具体实现。

以上的调用关系似乎有点复杂,来个图方便理解吧。



        设备也注册完了,设备的系统调用也具体实现了,接下来该看看视频缓冲区的申请与填写等操作了。

        videobuf_queue,videobuf_buffer关系、vivi_buffer与viv_damqueue关系需要理解Linux设备模型中的面向对象的概念,由于C语言不支持面向对象操作,所以为了使用面向对象的方法,采用在一个结构中嵌入另外一个结构的技术。videobuf_queue相当于是缓冲队列,来管理很多块缓冲区,videobuf_buffer相当于是一块缓冲区,但是一般在驱动中不直接使用这两个概念,而是将它们嵌入到更大的结构中vivi_buffer和vivi_damqueue结构中

        通过在vivi_open中使用函数videobuf_queue_vmalloc_init来初始化缓冲区并肩通过缓冲区加入缓冲队列链表,其中在videobuf_queue_ops中实现了缓冲队列的一些操作,buffer_setup,buffer_prepare,buffer_queue,buffer_release等,但是这些申请的缓冲队列都是在内核空间,如何将它映射到用户空间呢,在vivi_mmap函数中videobuf_mmap_mapper(struct videobuf_queue *q,struct vm_area_struct *vma);将缓冲队列映射到vm_area_struct虚拟地址


此外,关于自动创建设备节点要多说两句,有篇文章写的挺好的,http://www.cnblogs.com/skywang12345/archive/2013/05/15/driver_class.html

class_simple_create()   ——  class_create()    ——    class_register()

class_simple_destory() ——  class_destory()   ——   class_unregister()

class_device_create()   ——  device_create()  ——   class_device_register()

class_device_destory() ——  device_destory() ——   class_device_unregister()

这里只是简单说说我的理解class_create与class_register功能上类似,只是class_register需要分配struct class对象内存并初始化

自动创建设备节点class_create与device_create或者class_register,device_register,用户空间的udev会根据device_create创建相应的设备节点


有一篇文章写的不错可以参考一下:http://www.360doc.com/content/13/1010/16/7775902_320343471.shtml

        

Logo

更多推荐