1. 程式人生 > >攝像頭驅動——V4L2框架分析

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

static video gist app create 訪問 屬於 pla 設置

一、概述

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的實現較為復雜。

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