攝像頭驅動——V4L2框架分析


一、概述

Video for Linux 2,簡稱V4l2,是Linux內核中關於視頻設備的內核驅動框架,為上層的訪問底層的視頻設備提供了統一的接口。

攝像頭驅動是屬於字符設備驅動程序。(分析linux3.4.2內核)

二、如何寫字符設備驅動

1、對於簡單的驅動:

  1).構造一個file_operations:.open=drv_open .read=drv_read
  2).告訴內核:register_chrdev(主設備號,名字,&file_operations)
  3).入口函數:調用register_chrdev
  4).出口函數:卸載
  一般采用register_chrdev的代替方法:分配、設置cdev,cdev_add

2、對於稍復雜的驅動程序采用分層思想

例如LCD驅動中分為兩層:上層通用的核心層內核已經幫我們做好,即在fbmem.c
  1.構造file_operations(open/ read /write ...)
  2.注冊 

   3.入口、出口

我們做的是硬件相關層,供上層file_operations調用
  1.分配一個fb_info 結構體
  2.設置
  3.注冊
  4.硬件相關的操作

三、分析V4L2框架

把usb設備接到系統前台,會有打印信息,根據打印信息在內核里找出驅動,用dmsg命令查看;
grep "Found UVC" * -nR 搜索 在uvc_driver.c里,這是個硬件相關的驅動。

分析代碼,猜測V4L2  框架 肯定也是分為至少兩層  。

 

應用層:

/*調用 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分析v4l2核心驅動框架

(virtual video driver )虛擬視頻驅動

 分析如下:

vivi_init (入口函數)
     vivi_create_instance(i);
        v4l2_device_register  //非主要,僅初始化一些鎖等,實際未注冊啥
            spin_lock_init(&v4l2_dev->lock);
                ...
            get_device(dev);
            v4l2_dev->dev = dev;
        vfd = video_device_alloc(); //分配video_device
        //設置
        1.*vfd = vivi_template;        //內容設置為vivi_template
              /*最底層的vivi 操作函數*/
              static struct video_device vivi_template = {
                  .name             = "vivi",
                  .fops           = &vivi_fops,
                  .ioctl_ops        = &vivi_ioctl_ops,
                      ...
                  };
          
        2.vfd->v4l2_dev = &dev->v4l2_dev; 
        
        3.設置“Ctrl屬性”(用於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      類型     number
        video_register_device(video_device, VFL_TYPE_GRABBER, nr); //向上注冊
            __video_register_device    
            /* Part 1: check device type */  檢驗設備類型
                ...
            /* Part 2: find a free minor, device node number and device index. */
                ...                           互斥鎖相關
            /* Part 3: Initialize the character device */ 初始化字符設備
            vdev->cdev = cdev_alloc();
            vdev->cdev->ops = &v4l2_fops;
            cdev_add
                ...
            /* Part 6: Activate this minor. The char device can now be used. */
            video_device[vdev->minor] = vdev; //以次設備號為下標,將vdev存入數組

************************************************************************************
分析vivi.c的open、read、write、ioctl過程
1. open app: open ("/dev/video0",...) ---------------------------------------------- drv: v4l2_open vdev = video_devdata(filp); //根據次設備號從數組中得到video_device ret = vdev->fops->open(filp);//調用open函數 調用vivi.c 里的v4l2_fh_open 2. read app: read... ---------------------------------------------- drv: v412_read struct video_device *vdev = video_devdata(filp); ret = vdev->fops->read(filp, buf, sz, off); 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); 調用vivi.c 里的video_ioctl2 /*把用戶空間的參數復制進來,然后調用__video_do_ioctl*/ video_usercopy(file, cmd, arg, __video_do_ioctl); __video_do_ioctl *vdev = video_devdata(filp) 根據APP傳入的cmd來獲得、設置某些屬性
                 /*在vivi.c 里一開始的vivi_create_instance里設置*/

 由上分析可知vivi.c主要完成了以下工作:

1 、初始化 v4l2_device結構體(代表一個 v4l2設備) 

   v4l2_device_register、 v4l2_device

2、分配video_device結構體

   vfd = video_device_alloc()

3、設置video_device結構

   a、 .vfd->v4l2_dev

   b、vfd:

      .fops    設置vfd的fops 里的open、read、write 被上層調用

      .ioctl_ops   最終會調用到ioctl(設置屬性被上層調用  )

4、 注冊video_device結構體

  (video_device 是內核對 v4l2_device的官方封裝,用於管理v4l2_device數據),向上層用戶提供訪問接口
    video_register_device

 

: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;

小結:

v4l2框架並未脫離字符設備驅動框架,只是ioctl的實現較為復雜。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM