2.1 攝像頭V4L2驅動框架分析

學習目標:學習V4L2(V4L2:vidio for linux version 2)攝像頭驅動框架,分析vivi.c(虛擬視頻硬件相關)驅動源碼程序,總結V4L2硬件相關的驅動的步驟;node

 1、V4L2架構linux

1. 字符類驅動數組

V4L2(V4L2:vidio for linux version 2)攝像頭驅動屬於字符類驅動,架構

對於通常的字符類驅動程序,其編寫步驟通常分爲:app

1)構造一個file_operations: 編寫open=drv_open .read=drv_read
2)註冊設備,告訴內核:register_chrdev(主設備號,名字,&file_operations)
3)入口函數:調用register_chrdev
4)出口函數:卸載框架

對於複雜的字符類驅動程序,其程序是一種分層結構。例如LCD驅動程序。以下圖所示。ide

--> 上層爲核心層(內核已經作好的),在fbmem.c中 ,主要的做用爲:函數

1)構造file_operations(open read write 函數);2)註冊;3)入口、出口。學習

--> 硬件相關層(用戶須要作的),供核心層的file_operations調用,主要完成:spa

1) 分配一個fb_info 結構體;2) 設置fb_info 結構體等;3) 註冊;4) 硬件相關的操做。 

2. V4L2驅動架構

由以上字符類設備驅動架構可知,攝像頭驅動也是分層結構的。

其中,ucv_driver.c中,定義了uvc_driver結構體,根據ucv_ids查找匹配的設備,若是支持,則會進入probe函數

 1 struct uvc_driver uvc_driver = {  2     .driver = {  3         .name        = "uvcvideo",  4         .probe        = uvc_probe,  5         .disconnect    = uvc_disconnect,  6         .suspend    = uvc_suspend,  7         .resume        = uvc_resume,  8         .reset_resume    = uvc_reset_resume,  9         .id_table    = uvc_ids, 10         .supports_autosuspend = 1, 11  }, 12 };

二. vivi.c虛擬視頻驅動程序架構

因爲V4L2驅動程序是一種分層架構,用戶只須要完成硬件相關驅動程序便可。這裏主要以vivi虛擬視頻驅動程序爲例分析源碼的調用過程和框架。

1.  進入入口的vivi_init(void)函數:

 1 static int __init vivi_create_instance(int inst)  2 {  3     struct vivi_dev *dev;  4     struct video_device *vfd; //video_device結構體定義  5     struct v4l2_ctrl_handler *hdl;  6     struct vb2_queue *q;  7     int ret;  8 
 9     dev = kzalloc(sizeof(*dev), GFP_KERNEL);  10     if (!dev)  11         return -ENOMEM;  12 
 13     snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),  14             "%s-%03d", VIVI_MODULE_NAME, inst);  15     ret = v4l2_device_register(NULL, &dev->v4l2_dev);  16     if (ret)  17         goto free_dev;  18    //攝像頭相關屬性設置
 19     dev->fmt = &formats[0];  20     dev->width = 640;  21     dev->height = 480;  22     hdl = &dev->ctrl_handler;  23     v4l2_ctrl_handler_init(hdl, 11);  24     dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,  25             V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);  26     dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,  27             V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);  28     dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,  29             V4L2_CID_CONTRAST, 0, 255, 1, 16);  30     dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,  31             V4L2_CID_SATURATION, 0, 255, 1, 127);  32     dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,  33             V4L2_CID_HUE, -128, 127, 1, 0);  34     dev->autogain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,  35             V4L2_CID_AUTOGAIN, 0, 1, 1, 1);  36     dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,  37             V4L2_CID_GAIN, 0, 255, 1, 100);
55 /* initialize queue */ 56 q = &dev->vb_vidq; 57 memset(q, 0, sizeof(dev->vb_vidq)); 58 q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 59 q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; 60 q->drv_priv = dev; 61 q->buf_struct_size = sizeof(struct vivi_buffer); 62 q->ops = &vivi_video_qops; 63 q->mem_ops = &vb2_vmalloc_memops; 64 65 vb2_queue_init(q); 66 67 mutex_init(&dev->mutex); 68 69 /* init video dma queues */ 70 INIT_LIST_HEAD(&dev->vidq.active); 71 init_waitqueue_head(&dev->vidq.wq); 72 73 ret = -ENOMEM;
     //分配video_device結構體
74 vfd = video_device_alloc(); 75 if (!vfd) 76 goto unreg_dev; 77  //設置 78 *vfd = vivi_template;
  /******************************************************************
    其中,以賦值的方式進行設置vfd,進入vivi_template:
      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,
      };
  *******************************************************************//  79     vfd->debug = debug;  80     vfd->v4l2_dev = &dev->v4l2_dev;  81     set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);  82 
 83     /*
 84  * Provide a mutex to v4l2 core. It will be used to protect  85  * all fops and v4l2 ioctls.  86      */
 87     vfd->lock = &dev->mutex;  88     //註冊
 89     ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
93 video_set_drvdata(vfd, dev); 94 95 /* Now that everything is fine, let's add it to device list */ 96 list_add_tail(&dev->vivi_devlist, &vivi_devlist); 97 101 dev->vfd = vfd; 102 v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", 103 video_device_node_name(vfd)); 104 return 0;
107 video_device_release(vfd);110 v4l2_device_unregister(&dev->v4l2_dev);114 }

 vivi_init函數的調用結構以下:

vivi_init

   -->vivi_create_instance

        -->v4l2_device_register   // 不是主要, 只是用於初始化一些東西,好比自旋鎖、引用計數

              vfd = video_device_alloc(); //分配video_device結構體

             1.  *vfd = vivi_template; // 設置

              .fops           = &vivi_fops,
              .ioctl_ops    = &vivi_ioctl_ops,
              .release      = video_device_release,

     2.  vfd->v4l2_dev = &dev->v4l2_dev;

     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);
               dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
                V4L2_CID_CONTRAST, 0, 255, 1, 16);      

    4. video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);  //註冊

       --> __video_register_device(vdev, type, nr, 1, vdev->fops->owner);

         -->vdev->cdev = cdev_alloc();  (v4l2.dev.c程序中)

           vdev->cdev->ops = &v4l2_fops;

           cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);

2. vivi.c的open,read,write,ioctl過程

1 static const struct v4l2_file_operations vivi_fops = { 2     .owner        = THIS_MODULE, 3     .open           = v4l2_fh_open, 4     .release        = vivi_close, 5     .read           = vivi_read, 6     .poll         = vivi_poll, 7     .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */
8     .mmap           = vivi_mmap, 9 };

1)open

app:     open("/dev/video0",....)向下層調用
-------------------------------------------------------------------
drv:     v4l2_fops.v4l2_open
           vdev = video_devdata(filp);  // 根據次設備號從數組中獲得video_device

       return video_device[iminor(file->f_path.dentry->d_inode)];

   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);
            if (video_is_registered(vdev))
      ret = vdev->fops->read(filp, buf, sz, off);

       調用vivi.c 裏的vivi_read

3)ioctl
app:   ioctl 
----------------------------------------------------
drv:   v4l2_fops.unlocked_ioctl => v4l2_ioctl
                struct video_device *vdev = video_devdata(filp);
             if (video_is_registered(vdev))
       ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
                       調用vivi.c 裏的video_ioctl2
                                video_usercopy(file, cmd, arg, __video_do_ioctl); //從用戶空間把用戶的命令cmd複製進來,調用__video_do_ioctl
                                    __video_do_ioctl
                                        struct video_device *vfd = video_devdata(file); // 根據次設備號從數組中獲得video_device
                                        switch (cmd) { .....     // 根據APP傳入的cmd來得到、設置"某些屬性"
 
--->>(例程分析:2.3節)
v4l2_ctrl_handler的使用過程:
      .......
     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)  // 在video_register_device設置 vivi_create_instance-->hdl = &dev->ctrl_handler;    v4l2_ctrl_handler_init(hdl, 11);
         ret = v4l2_queryctrl(vfd->ctrl_handler, p);  // 根據ID在ctrl_handler裏找到v4l2_ctrl,返回它的值
-------->>
 1 hdl = &dev->ctrl_handler; 2 v4l2_ctrl_handler_init(hdl, 11);
 3     dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,  4             V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200);  5     dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,  6             V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);  7     dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,  8             V4L2_CID_CONTRAST, 0, 255, 1, 16);  9     dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, 10             V4L2_CID_SATURATION, 0, 255, 1, 127); 11     dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, 12             V4L2_CID_HUE, -128, 127, 1, 0); 13     dev->autogain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, 14             V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 15     dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, 16             V4L2_CID_GAIN, 0, 255, 1, 100);

3、怎麼寫v4l2驅動?

1. 分配、設置、註冊:v4l2_device --》 v4l2_device_register()(輔助做用,提供自旋鎖、引用計數等功能)

2. 分配一個video_device:video_device_alloc()

3. 設置

1)vfd->v4l2_dev  

2)  .fops             設置vfd的fops 裏的open、read、write 被上層調用
  .ioctl_ops      設置屬性被上層調用    

3)註冊:video_register_device()

4. 接下來,應用層App能夠經過ioctl來設置(得到)亮度等某些屬性,在驅動程序裏,誰來接收、存儲、設置到硬件(提供這些信息)?

在驅動程序中抽象出來一個結構體v4l2_ctrl,每一個Ctrl對應其中的一項(音量、亮度等等);

 v4l2_ctrl_handler來管理他們,在vivi.c的vivi_create_instance函數中:

  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;

 1 static int __init vivi_create_instance(int inst)  2 {  3     struct vivi_dev *dev;  4     struct video_device *vfd;  5     struct v4l2_ctrl_handler *hdl; //定義v4l2_ctrl_handler結構體  6     struct vb2_queue *q;  7     int ret;  8 
 9     dev = kzalloc(sizeof(*dev), GFP_KERNEL); 10     if (!dev) 11         return -ENOMEM; 12 
13     snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), 14             "%s-%03d", VIVI_MODULE_NAME, inst); 15     ret = v4l2_device_register(NULL, &dev->v4l2_dev); 16     if (ret) 17         goto free_dev; 18 
19     dev->fmt = &formats[0]; 20     dev->width = 640; 21     dev->height = 480; 22     hdl = &dev->ctrl_handler; 23     v4l2_ctrl_handler_init(hdl, 11); //初始化 24     dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, //建立一個屬性設置其值,而且放入v4l2_ctrl_handler的鏈表 25             V4L2_CID_AUDIO_VOLUME, 0, 255, 1, 200); 26     dev->brightness = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, 27             V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); 28     dev->contrast = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, 29             V4L2_CID_CONTRAST, 0, 255, 1, 16); 30     dev->saturation = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, 31             V4L2_CID_SATURATION, 0, 255, 1, 127); 32     dev->hue = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, 33             V4L2_CID_HUE, -128, 127, 1, 0); 34     dev->autogain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, 35             V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 36     dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, 37             V4L2_CID_GAIN, 0, 255, 1, 100); 38     dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL); 39     dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL); 40     dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL); 41     dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL); 42     dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);

45 dev->v4l2_dev.ctrl_handler = hdl;    ......... }
相關文章
相關標籤/搜索