1. 程式人生 > >攝像頭驅動(二)————V4L2 虛擬攝像頭驅動vivi深入分析

攝像頭驅動(二)————V4L2 虛擬攝像頭驅動vivi深入分析

本文基於:linux3.5

  前面一篇文章中,簡單分析了 V4L2 大框架,本文藉助核心中的虛擬攝像頭驅動 vivi 來分析一個完整的攝像頭驅動程式。vivi 相對於後面要分析的 usb 攝像頭驅動程式,它沒有真正的硬體相關層的操作,也就是說拋開了複雜的 usb 層的相關知識,便於理解 V4L2 驅動框架,側重於驅動和應用的互動。

  前面我們提到,V4L2 的核心是 v4l2-dev.c 它向上提供統一的檔案操作介面 v4l2_fops ,向下提供 video_device 註冊介面 register_video_device ,作為一個具體的驅動,需要做的工作就是分配、設定、註冊一個 video_device.框架很簡單,複雜的是視訊裝置相關眾多的 ioctl。

一、vivi 框架分析

[cpp] view plain copy  print?在CODE上檢視程式碼片派生到我的程式碼片
  1. staticint __init vivi_init(void)  
  2. {  
  3.     ret = vivi_create_instance(i);  
  4.     ...  
  5.     return ret;  
  6. }  
  7. module_init(vivi_init);  

  vivi 分配了一個 video_device 指標,沒有去設定而是直接讓它指向了一個現成的 video_device 結構 vivi_template ,那麼全部的工作都將圍繞 vivi_template 展開。

[cpp] view plain copy  print?在CODE上檢視程式碼片派生到我的程式碼片
  1. staticint __init vivi_create_instance(int inst)  
  2. {  
  3.     struct vivi_dev *dev;  
  4.     struct video_device *vfd;  
  5.     struct v4l2_ctrl_handler *hdl;  
  6.     struct vb2_queue *q;  
  7.     // 分配一個 vivi_dev 結構體
  8.     dev = kzalloc(sizeof(*dev), GFP_KERNEL);  
  9.     // v4l2_dev 初始化,並沒有什麼作用
  10.     ret = v4l2_device_register(NULL, &dev->v4l2_dev);  
  11.     // 設定 dev 的一些引數,比如影象格式、大小
  12.     dev->fmt = &formats[0];  
  13.     dev->width = 640;  
  14.     dev->height = 480;  
  15.     dev->pixelsize = dev->fmt->depth / 8;  
  16.     ...  
  17.     // vivi_dev->vb_vidq(vb2_queue) 初始化
  18.     q = &dev->vb_vidq;  
  19.     memset(q, 0, sizeof(dev->vb_vidq));  
  20.     q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
  21.     q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;  
  22.     q->drv_priv = dev;  
  23.     q->buf_struct_size = sizeof(struct vivi_buffer);  
  24.     // vivi_dev->vb_vidq(vb2_queue)->ops
  25.     q->ops     = &vivi_video_qops;  
  26.     // vivi_dev->vb_vidq(vb2_queue)->mem_ops
  27.     q->mem_ops = &vb2_vmalloc_memops;  
  28.     // 初始化一些鎖之類的東西
  29.     vb2_queue_init(q);  
  30.     /* init video dma queues */
  31.     INIT_LIST_HEAD(&dev->vidq.active);  
  32.     init_waitqueue_head(&dev->vidq.wq);  
  33.     // 分配一個 video_device ,這才是重點
  34.     vfd = video_device_alloc();  
  35.     *vfd = vivi_template;  
  36.     vfd->debug = debug;  
  37.     vfd->v4l2_dev = &dev->v4l2_dev;  
  38.     set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);  
  39.     vfd->lock = &dev->mutex;  
  40.     // 註冊 video_device !!!
  41.     ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);  
  42.     // 把 vivi_dev 放入 video_device->dev->p->driver_data ,這個後邊經常用到
  43.     video_set_drvdata(vfd, dev);  
  44.     /* Now that everything is fine, let's add it to device list */
  45.     list_add_tail(&dev->vivi_devlist, &vivi_devlist);  
  46.     if (video_nr != -1)  
  47.         video_nr++;  
  48.     // vivi_dev->vfd(video_device) =  vfd
  49.     dev->vfd = vfd;  
  50.     v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n",  
  51.           video_device_node_name(vfd));  
  52.     return 0;  
  53. }  
  使用者空間呼叫的是 v4l2_fops ,但是最終會呼叫到 vivi_fops ,vivi_fops 中的 ioctl 呼叫 video_ioctl2
[cpp] view plain copy  print?在CODE上檢視程式碼片派生到我的程式碼片
  1. staticstruct video_device vivi_template = {  
  2.     .name       = "vivi",  
  3.     .fops           = &vivi_fops,  
  4.     .ioctl_ops  = &vivi_ioctl_ops,  
  5.     .minor      = -1,  
  6.     .release    = video_device_release,  
  7.     .tvnorms              = V4L2_STD_525_60,  
  8.     .current_norm         = V4L2_STD_NTSC_M,  
  9. };  

  video_register_device 過程就不詳細分析了,前面的文章中分析過,大概就是向核心層註冊 video_device 結構體,核心層註冊字元裝置並提供一個統一的 fops ,當用戶空間 read write ioctl 等,最終還是會跳轉到 video_device->fops ,還有一點就是核心層會把我們註冊進來的 video_device 結構放入一個全域性的 video_device陣列。

[cpp] view plain copy  print?在CODE上檢視程式碼片派生到我的程式碼片
  1. staticconststruct 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. };  
  這裡,先看一下 v4l2_fh_open 函式 [cpp] view plain copy  print?在CODE上檢視程式碼片派生到我的程式碼片
  1. int v4l2_fh_open(struct file *filp)  
  2. {  
  3.   // 前面註冊時,我們將 video_device 結構體放入了全域性陣列 video_device ,現在通過     video_devdata 函式取出來,後面經常用到這種做法
  4.     struct video_device *vdev = video_devdata(filp);  
  5.     // 分配一個 v4l2_fh 結構,放入file->private_data 中
  6.     struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);  
  7.     filp->private_data = fh;  
  8.     if (fh == NULL)  
  9.         return -ENOMEM;  
  10.     v4l2_fh_init(fh, vdev);  
  11.     v4l2_fh_add(fh);  
  12.     return 0;  
  13. }  
  1、我們隨時可以通過 video_devdata 取出我們註冊的 video_device 結構進行操作

  2、我們隨時可以通過 file->private_data 取出 v4l2_fh 結構,雖然現在還不知道它有啥用

  下面來分析 ioctl ...首先來看一下呼叫過程 

[cpp] view plain copy  print?在CODE上檢視程式碼片派生到我的程式碼片
  1. long video_ioctl2(struct file *file,  
  2.            unsigned int cmd, unsigned long arg)  
  3. {  
  4.     return video_usercopy(file, cmd, arg, __video_do_ioctl);  
  5. }  
[cpp] view plain copy  print?在CODE上檢視程式碼片派生到我的程式碼片
  1. staticlong __video_do_ioctl(struct file *file,  
  2.         unsigned int

    相關推薦

    攝像頭驅動————V4L2 虛擬攝像頭驅動vivi深入分析

    本文基於:linux3.5   前面一篇文章中,簡單分析了 V4L2 大框架,本文藉助核心中的虛擬攝像頭驅動 vivi 來分析一個完整的攝像頭驅動程式。vivi 相對於後面要分析的 usb 攝像頭驅動程式,它沒有真正的硬體相關層的操作,也就是說拋開了複雜的 us

    V4L2攝像頭測試

    #include<stdio.h> #include<stdlib.h> #include<string.h> #include<sys/types.h> #include<fcntl.h> #include<linux/vi

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

    V4L2 :video for linux version 2 ,是 linux 裡一套標準的視訊驅動,它支援 uvc 標準的攝像頭。本文來分析一下它的核心框架。   整個v4l2的框架分為三層:     在應用層,我們可以在 /dev 目錄發現 video0 類

    azure虛擬

    azure(二) azure虛擬機1、 創建一個簡單的Microsoft Azure虛擬機 1.1、 快速創建虛擬機 在Microsoft Azure的虛擬機頁面中,單擊“創建虛擬機”或者“新建”按鈕。依次單擊“計算”—“虛擬機”—“快速創建”,這裏需要填寫虛擬機的DNS名稱,選擇虛擬機的映像和虛擬機 大小,

    JDBC詳解系列之加載驅動

    red mar mys ons try path 替換 host man ---[來自我的CSDN博客](http://blog.csdn.net/weixin_37139197/article/details/78838091)--- ??在JDBC詳解系列(一)之流程中

    從零開始之驅動發開、linux驅動十八、framebuffer驅動框架

    框架 1.註冊一個framebuffer類。 2.註冊一個主裝置號,因為fb個數通常比較少,所以可以用老的介面統一註冊。 3.為2中的註冊實現通用的fops,注意這裡是通用的,特殊的架構在通用的裡面還是要呼叫專門fb註冊時實現的操作介面。(參考下面) 4.提供統一的註冊,解除安裝

    嵌入式核心及驅動開發之學習筆記 實現應用控制驅動

    Linux系統根據驅動程式實現的模型框架將裝置驅動分成字元裝置驅動、塊裝置驅動、網路裝置驅動三大類。這裡簡單理解一下概念 字元裝置:裝置按位元組流處理資料,通常用的串列埠裝置、鍵盤裝置都是這種。 塊裝置:裝置按塊單位對資料處理,通常是儲存裝置。 網路裝置:顧名思義,建立在soc

    Windows核心基礎虛擬記憶體空間佈局

    32位Windows作業系統支援32位定址,因此2的32次方就等於4GB,每個程式在執行時都會被對映進4GB空間的記憶體空間,這4GB空間不全是使用者可以使用的,其中0x7fffffff-0xffffffff是2GB的核心空間,這部分用來儲存核心的資料,使用者程式是無法直接訪問的。

    環境維護虛擬機器中安裝win7

    開啟虛擬機器,然後新建一個虛擬機器,新建虛擬機器的步驟,可以參考 環境維護(一) 裡的步驟。唯在選擇客戶機作業系統的時候,選擇window系統,如下: 其它步驟,有推薦就選推薦的,沒有推薦就與安裝linux時的選擇一樣。 虛擬機器建好以後,點選CD/DVD: 右邊選擇:

    從零開始之驅動發開、linux驅動十、linux裝置驅動中的併發控制

    本文參考自宋寶華老師的《linux驅動開發詳解》 併發(Concurrency) 指的是多個執行單元同時、 並行被執行, 而併發的執行單元對共享資源(硬體資源和軟體上的全域性變數、 靜態變數等) 的訪問則很容易導致競態(Race Conditions)   只要併發的

    JVM記憶體模型—— HotSpot虛擬機器分析

    上一節我們講了Java虛擬機器的理論記憶體模型,同時我們也提到了,這些只是Java虛擬機器規範中的內容,如果我們要研究一個物件是如何建立、如何佈局等一系列細節問題的時候,我們就必須在具體的虛擬機器中分析,因為不同的虛擬機器的實現是不一樣的,下面我們就選最常用、最普遍的虛擬機器

    JVM——記憶體模型虛擬機器棧與本地方法棧

    本篇文章將繼續認識Java虛擬機器中的記憶體模型,今天要認識的是我們常說的"棧”。 棧其實也分兩種,一種是虛擬機器棧,一種是本地方法棧。而我們平常說的最多的,就是虛擬機器棧。接下來就讓我們走進這兩個棧,看看他們是個啥。 1.虛擬機器棧 虛擬機器棧,即Java Virtual&n

    Python運維Docker虛擬機器

    一、Docker 虛擬機器架構 Docker 建立的所有虛擬例項共用同一個Linux核心,對硬體佔用較小,屬於輕量級虛擬機器 二、Docker 映象與容器 容器是從映象中創建出來的虛擬例項 映象是用來安裝程式,是隻讀層 容器是用來執行程式,是隻讀層 倉庫、映象

    Simscape入門—— 給關節加驅動訊號

    如果你的模型就位,演算法也已經就位。想接下來把他倆合起來模擬看看效果,這時候問題就出現了: 我演算法裡面算出來的關節變數值是相對於我在用DH方法定義的座標系而言的,而我模型裡的關節變數的值是相對於我之前畫設計圖時設的座標系而言的,沒辦法直接用。 因此需要在你的逆運動學模組後面、模型關節輸入之

    Simscape入門—— 給關節加驅動訊號報錯

    1.把simulink-PS converter模組裡面的Input Handling引數分別改成 Filter input,derivatives calculated Second-order filtering 時間常數隨意 2.模型配置引數(Mode

    C++ 類物件大小計算含有虛擬函式類

    五、包含虛擬函式的類         包含虛擬函式的類,物件生成時,會在類物件當中插入一個指標,這個指標稱做虛擬函式表指標,簡稱虛表指標(vPtr)。該指標指向一個虛擬函式表(簡稱虛表),虛擬函式表中儲存了虛擬函式的入口地址。基類當中有虛擬函式時,會產生該虛擬函式表;建立基

    Spring 註解驅動WEB 註解開發

    Spring 註解驅動(二)WEB 註解開發 Spring 系列目錄(https://www.cnblogs.com/binarylei/p/10198698.html) 一、基本使用 在 Servlet 3.0 時支援註解啟動,不再需要 web.xml 配製檔案。 1.1 Servlet 3.0 註

    Hive使用者介面—使用Hive JDBC驅動連線Hive操作例項

    問題導讀:         1、Hive提供了哪三種使用者訪問方式?         2、使用HiveServer時候,需要首先啟動哪個服務?         3、HiveServer的啟動命令是?         4、HiveServer是通過哪個服務來提供遠端JDBC訪

    linux下的塊裝置驅動

    上一章主要講了請求佇列的一系列問題。下面主要說一下請求函式。首先來說一下硬碟類塊裝置的請求函式。 請求函式可以在沒有完成請求佇列的中的所有請求的情況下就返回,也可以在一個請求都不完成的情況下就返回。 下面貼出請求函式的例程: static int simp_blkdev_m

    JVMJava虛擬機器組成詳解

    導讀:詳細而深入的總結,是對知識“豁然開朗”之後的“刻骨銘心”,想忘記都難。 Java虛擬機器(Java Virtual Machine)下文簡稱jvm,上一篇我們對jvm有了大體的認識,進入本文之後我們將具體而詳細的介紹jvm的方方面面,而本文主要講的是jvm的組成,瞭解了它,就揭開了jvm的神祕面紗。