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

1、概述node

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

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

2、如何寫字符設備驅動app

一、對於簡單的驅動:框架

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

二、對於稍複雜的驅動程序採用分層思想函數

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

   3.入口、出口spa

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

code

3、分析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            

 

4、經過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

二、分配video_device結構體

   vfd = video_device_alloc()

三、設置video_device結構

   a、 .vfd->v4l2_dev

   b、vfd:

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

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

四、 註冊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的實現較爲複雜。

相關文章
相關標籤/搜索